docker和docker-compose的前后端项目部署(含MySQL,Redis和RabbitMQ)

前言

Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余项目。它基于 Google 公司推出的 Go 语言实现。 项目后来加入了 Linux 基金会,遵从了 Apache 2.0 协议,项目代码在 GitHub 上进行维护。

Docker 自开源后受到广泛的关注和讨论,以至于 dotCloud 公司后来都改名为 Docker Inc。Redhat 已经在其 RHEL6.5 中集中支持 Docker;Google 也在其 PaaS 产品中广泛应用。

Docker 项目的目标是实现轻量级的操作系统虚拟化解决方案。 Docker 的基础是 Linux 容器(LXC)等技术。在 LXC 的基础上 Docker 进行了进一步的封装,让用户不需要去关心容器的管理,使得操作更为简便。用户操作 Docker 的容器就像操作一个快速轻量级的虚拟机一样简单。

下面两张图比较了传统虚拟机和docker之间的区别

docker和docker-compose的前后端项目部署(含MySQL,Redis和RabbitMQ)_第1张图片
传统虚拟机
docker和docker-compose的前后端项目部署(含MySQL,Redis和RabbitMQ)_第2张图片
Docker

从而我们可以看出传统虚拟机是在硬件层面实现的,而docker是在操作系统层面上实现的虚拟化

p.s.:Hypervisor是一种运行在物理服务器和操作系统之间的中间软件层,可允许多个操作系统和应用共享一套基础物理硬件,因此也可以看作是虚拟环境中的“元”操作系统,它可以协调访问服务器上的所有物理设备和虚拟机,也叫虚拟机监视器(Virtual Machine Monitor)。Hypervisor是所有虚拟化技术的核心。非中断地支持多工作负载迁移的能力是Hypervisor的基本功能。当服务器启动并执行Hypervisor时,它会给每一台虚拟机分配适量的内存、CPU、网络和磁盘,并加载所有虚拟机的客户操作系统。

现在项目一般为单点登录或者是分布式,而分布式能想到微服务,所以如果把这种思想应用到容器部署中则会有基于单容器部署(类似单点登录)和多容器部署(类似微服务)两种方式,多容器部署即把mysql、rabbitMq、redis等依赖组件和spring boot服务分开成多个容器来部署的方式;单容器部署即把MySQL等其他组件和spirng boot应用放在同一容器的部署方式。

多容器部署的方式是目前主流的方式,因为具有以下优势:

  1. 方便横向扩展;
  2. 服务可用性高,不会因为一个容器服务挂了而导致所有服务都不能用;
  3. 单个容器不会太臃肿、资源消耗相对较小;
  4. 部署方便。

多容器的部署方式又有基于docker命令和基于docker-compose命令两种方式。这里首先要再介绍下docker compose。

Docker Compose 是Docker官方编排(Orchestration)项目之一,负责快速在集群中部署分布式应用。比较直观的感受是多个容器可以通过一个配置来启动,从而简化了部署过程,我们也可以通过一个配置文件来查看部署的命令等等。

下面我就介绍基于docker run等docker命令和基于docker compose两种部署方式

e.g:本文中前端采用pm2工具部署,后端采用spring boot + mysql + redis + rabbitmq

基于docker命令部署

后端Spring boot及依赖服务部署

首先Spring Boot应用依赖于Mysql、redis和RabbitMQ服务,所以我们先把这三个服务部署好。

在安装部署前我们可以搜索要安装的服务有哪些镜像,可以选择合适我们的。

docker search 镜像名

例如要搜索mysql的镜像就可以用命令(其他服务同理)

docker search mysql

MySQL服务部署

我采用了镜像搜索结果中的第一个镜像

NAME                                                   DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
mysql                                                  MySQL is a widely used, open-source relati...   7708      [OK]    

执行镜像拉去命令

docker pull mysql

使用该命令会去拉取tag为latest的mysql镜像,使用以下命令可以查看本地存在的所有镜像,我们就可看到刚才下载的mysql镜像了。

docker images   

使用启动镜像的命令:

docker run --name test-mysql --restart=always -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql --lower_case_table_names=1 --default-authentication-plugin=mysql_native_password

p.s:命令详解

  • --name:给容器命名

  • --restart:容器重启策略

    • no,默认策略,在容器退出时不重启容器
    • on-failure,在容器非正常退出时(退出状态非0),才会重启容器
      • on-failure:3,在容器非正常退出时重启容器,最多重启3次
    • always,在容器退出时总是重启容器
    • unless-stopped,在容器退出时总是重启容器,但是不考虑在Docker守护进程启动时就已经停止了的容器
  • -p:端口映射,映射逻辑为主机端口:容器端口

  • -e:参数设置,这里的MYSQL_ROOT_PASSWORD就是设置账号名为root的账户的密码

  • -d:后台运行容器

  • mysql:要运行的镜像名

  • --lower_case_table_names=1:设置不区分表名大小写,不然可能会在spring boot应用时报表不存的错误(实际创建的数据库和表为:test.user,报错提示为test.USER is not exist)

  • --default-authentication-plugin=mysql_native_password:mysql版本为8.0以上时需要,因为8.0和5.7的密码加密机制不同,如果本地navicat版本不支持新的加密机制(ERROR 2059 (HY000): Authentication plugin 'caching_sha2_password' cannot be loaded)则要加上这条命令;如果不加的话也可以通过后续密码的方式来解决。解决方法如下:1.执行命令:docker exec -it 容器id /bin/bash;2.再执行:mysql -h 127.0.0.1 -P 3306 -u root -p,输入密码进去mysql;3.修改密码:ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'root';

p.s:

  1. 第三步中的BY 'root',root为要设置的新密码。
  2. docker exec -it表示开启交互模式的终端(-it=-i -t)
  3. /bin/bash表示可以执行一些简单的shell命令

在执行完以上操作后就可以用Navicat等工具链接数据库测试是否搭建成功。

Redis服务部署

搜索和下载镜像的部署同MySQL中一样,本文采用的redis镜像为:

NAME                              DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
redis                             Redis is an open source key-value store th...   6431      [OK]       

执行命令启动服务

docker run -d --name test-redis -p 6379:6379 redis --requirepass 'mypassword'

如果要开启redis的持久化则加上 --appendonly yes
完成命令如下:

docker run -d --name test-redis -p 6379:6379 redis --requirepass 'mypassword' --appendonly yes

如果不加--appendonly yes则默认开启redis的RDB模式,如果加了则开启AOF模式。

redis服务测试:

docker exec -it 容器id redis-cli -h 127.0.0.1 -p 6379

进入redis后验证密码的命令为:
auth mypassword

RabbitMQ服务部署

本文拉取的rabbitmq的镜像为:(rabbitmq:management)

NAME                                       DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
rabbitmq                                   RabbitMQ is an open source multi-protocol ...   2445      [OK] 

运行服务命令:

docker run -d --hostname localhost --name test-rabbit -p 15672:15672 -p 5672:5672 rabbitmq:management

--hostname:指定容器主机名称

这里用了两次-p的端口映射是因为5672是给服务用的端口。15672是web端访问的接口

如果要在启动时同时设置用户和密码可使用一下命令

docker run -d --hostname localhost --name test-rabbit -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 15672:15672 -p 5672:5672 rabbitmq:management

启动服务后检查是否可以通过访问ip:15672打开web管理页面(使用的镜像如果是rabbitmq:latest则无法打开,如果是rabbitmq:management则可以打开),如果打不开则是因为rabbitmq默认不开启web页面,需要进去容器中设置。执行命令:

docker exec -it 容器id /bin/bash

在进入容器后执行:

rabbitmq-plugins enable rabbitmq_management

我们再访问web页面就发现可以打开了。

Spring Boot服务部署

在部署完其他的组件后终于可以部署我们的应用了,而整体的思路就是把我们的应用的jar文件变成镜像文件,然后再运行即可。所以首先我们需要把应用打包成jar文件,拷贝到服务器上一个目录下,本文中拷贝到了/opt/test/jar文件夹下,然后在该文件夹下再用命令vi Dockerfile创建文件。

#指定基础镜像,格式为:FROM image:tag
FROM java

#维护者信息
MAINTAINER TEST

#添加要加入的文件,与COPY命令的性质基本一致,但是ADD更强大
#可添加源路径,可自动解压压缩包,但解压功能和指定目标路径不可同时使用
#该命令在使用docker run -v时可不用,在后续有介绍
ADD spring-boot-test.jar /opt/test/test.jar
#add命令会把config下的所有文件拷贝到容器中的/opt/test/config下,此条代码只用作添加文件夹的讲解示范,
ADD config /opt/test/config/

#用于指定在容器启动时要执行的命令
CMD java -jar /opt/test.jar

#为构建镜像设置监听端口,使容器在运行时监听
EXPOSE 8082

以上就是创建一个简单的Dockerfile的示例。

然后运行命令:

docker build -t my-test-image .
  • -t:给镜像命名
  • 最后的点表示使用当前上下文中的dockerfile文件

构建好了可以用以下命令查看是否有自己的镜像了

docker images

有我们的镜像后就可以运行了。

docker run -d -p 8082:8082 --name my-spring-boot my-test-image

e.g.(推荐)

可以在docker run的时候加入-v 来映射数据卷,这样的话在后续版本更新中可方便地替换jar文件。

docker run -d -p 8082:8082 --name my-spring-boot -v jar:/opt/test

这条命令中 -v后的参数的意思为:把本机的当前目录的jar文件夹映射到容器中的/opt/test文件夹,这样每次更新只需更换test.jar即可(使用数据卷的话可在Dockerfile中把ADD spring-boot-test.jar /opt/test/test.jar这行命令去掉)

使用以下命令可查看当前运行的镜像

docker ps

如果要查看运行过的所有镜像(包括以前运行过,现在停止的)

docker ps -a

如果启动失败了可以通过以下几条命令查看容器日志排查问题

docker logs [OPTIONS] CONTAINER
Options:
    --details        显示更多的信息
    -f, --follow         跟踪实时日志
    --since string   显示自某个timestamp之后的日志,或相对时间,如42m(即42分钟)
    --tail string    从日志末尾显示多少行日志, 默认是all
    -t, --timestamps     显示时间戳
    --until string   显示自某个timestamp之前的日志,或相对时间,如42m(即42分钟)

前端pm2部署

1.首先把项目文件发布到服务器上,本例中放到了/opt/frontend/file下

2.拉取node镜像,本例中用的node镜像为:

NAME                                   DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
node                                   Node.js is a JavaScript-based platform for...   6935      [OK]   

3.使用vi Dockerfile创建文件,文件内容如下:

#使用的基础镜像
FROM node:latest

#作者信息
MAINTAINER test

#把file文件夹下的所有文件添加到容器的/opt/frontend下
ADD file /opt/frontend/

#在镜像的构建过程中执行特定的命令并生成一个中间镜像,node的镜像中没有pm2,所以在此安装
RUN npm install -g pm2

#指定工作路径,之后的命令将在此路径上运行
WORKDIR /opt/frontend

#因为本例中不会自动生成日志文件和文件夹所以在这创建,如果自己的项目不需要的可以去掉这里的命令
RUN mkdir logs
RUN touch logs/app.log

#修改脚本的权限,不然会报没有权限的错误
RUN chmod 775 frontend-start.sh

#npm安装
RUN npm install

#暴露前端4010端口(根据自己前端服务的端口修改)
EXPOSE 4010

#执行脚本
CMD /opt/frontend/frontend-start.sh

e.g:因为CMD命令是只有最后一个才生效,而我们要执行的命令又不止一条,所以采用了运行脚本文件的方式来运行

脚本文件frontend-start.sh内容如下:

#pm2部署项目
pm2 start /opt/frontend/pm2.json

#持续监控日志,让容器不退出
tail -f /opt/frontend/logs/app.log

4.在编写完这些文件后就可以开始构建镜像

docker build -t test-frontend .

最后的点表示使用当前上下文中的dockerfile文件

5.使用docker images命令查看我们构建的镜像

运行镜像的命令

docker build -d -p 4010:4010 --name test-frontend test-frontend:latest

6.运行之后我们通过docker ps命令查看容器是否在运行,如果在运行了就可在本地通过浏览器访问地址来打开页面:http://服务器ip:4010

7.如果运行有错误可通过docker logs 容器id来查看日志解决问题

基于docker-compose部署

首先我介绍一下本例子的目录结构

-docker-compose.yml
-fontend
  -Dockerfile
  -files(前端项目文件,已包含pm2.json)
-backend
  -Dockerfile
  -test.jar
-mysql
  -Dockerfile
  -db
  -sql(要初始化数据库所需要的sql)

前后端的Dockerfile在前文中已经介绍,这里主要相比前文做一些差异化介绍,差一点就在于docker-compose.yml文件和mysql的Dockerfile

mysql的Dockerfile的内容如下:

#使用mysql8.0在后续的sql自动执行中会有问题,所以这里采用mysql5.7
FROM mysql:5.7

MAINTAINER test-mysql-compose

#把sql
ADD sql /docker-entrypoint-initdb.d

编写该Dockerfile的目的主要是把要初始化mysql数据库的sql文件添加入容器中,把我们想要自动执行的sh文件或sql文件放入/docker-entrypoint-initdb.d中即可,在容器初始化时就会自动执行(原理是在容器中的/docker-enetrypoint.sh文件,容器启动时会去执行该文件,该文件又会去执行/docker-entrypoint-initdb.d中的sh和sql文件)

e.g.

这里有两个需要特别注意的问题

  1. 在mysql8.0中如果在docker-compose中添加了volumn的映射就不会再去执行docker-entrypoint.sh文件,而5.7版本没有这个问题。我暂没有找到解决办法,所以这里就采用了5.7版本。
  2. 我在添加了sql文件,执行mysql镜像后出现连不上mysql的问题,报的unknow server的错误,过了十多分钟后才能正常脸上。经过排查才发现是因为sql文件太大导致没有导入完(我的运行了16个SQL文件大概共15.5MB,用了20分钟左右)。这样的问题当然有主机性能等因素影响,所以建议初始化的sql文件尽量要小。

接下来就是最重要的docker-compose.yml文件的介绍

#docker-compose的版本
version: '3'

#定义服务
services:

  #服务名称,可随意定义
  backend:
    build:
      #dockerfile的路径
      context: backend
      #dockerfile的名称
      dockerfile: Dockerfile
    #相当于docker run -v的作用
    volumes:
      - "./jar:/opt/test"
    #容器名称
    container_name: test-backend-compose
    #该服务依赖的其他服务,该配置选项可修改启动顺序
    depends_on:
      - mysql
      - redis
      - rabbitmq
    ports:
      - "8082:8082"

  frontend:
    build:
      context: frontend
      dockerfile: Dockerfile
    ports:
      - "4010:4010"
    container_name: test-frontend-compose

  mysql:
    build:
      context: mysql
      dockerfile: Dockerfile
    ports:
      - "3306:3306"
    #相当于docker run命令中的-e
    environment:
      MYSQL_ROOT_PASSWORD: root
      #初始化的数据库名称
      MYSQL_DATABASE: test
    container_name: test-mysql-compose
    restart: always
    #数据卷映射关系,把本机的./mysql/db目录映射到容器中的/var/lib/mysql
    volumes:
      - "./mysql/db/:/var/lib/mysql"
    #该选项中的命令会覆盖Dockfile中的CMD中的命令.lower_case_table_names参数是为了表名不区分大小写,default-authentication-plugin是8.0中密码加密策略不同带来的链接问题,如果不用8.0可不加此选项
    command: mysqld --lower_case_table_names=1 --default-authentication-plugin=mysql_native_password

  redis:
    image: redis
    ports:
      - "6379:6379"
    container_name: test-redis-compose
    restart: always
    #启动redis服务并添加密码为:123456,并开启redis的持久化
    command: redis-server --requirepass 123456 --appendonly yes

  rabbitmq:
    image: rabbitmq:management
    ports:
      - "5672:5672"
      - "15672:15672"
    container_name: test-rabbitmq-compose
    environment:
      #rabbitmq的初始用户名
      RABBITMQ_DEFAULT_USER: admin
      #rabbitmq的初始密码
      RABBITMQ_DEFAULT_PASS: 123456

#指定使用的网络,此处是使用已经提前创建好的自定义网络
#网络创建命令:docker network create -d bridge --subnet 172.50.0.0/16 cooperationassociation
#--subnet指定网段 -d指定连接方式,最后的cooperationassociation为网络名称
#使用新的指定网络是为了防止网段占用完,这样会导致启动容器时XShell会自动退出,且本地用不了访问不了服务(服务器已有大量连接时可能会出现)
#查看网段占用情况的命令:route -n
networks:
  default:
    external:
      name: cooperationassociation

编写文件后使用以下命令即可运行

#-d表示后台运行
docker-compose up -d

docker-compose还有以下常用命令

#停止运行并移除容器
docker-compose down

#启动单个服务
docker-compose up -d 服务名

#查看当前运行的服务
docker-compose ps

#构建镜像,--no-cache表示不用缓存,否则在重新编辑Dockerfile后再build可能会直接使用缓存而导致新编辑内容不生效
docker-compose build --no-cache

#查看镜像
docker-compose images

#查看日志
docker-compose logs

#启动/停止服务
docker-compose start/stop 服务名

#拉取镜像
docker-compose pull 镜像名

以上只是一些最常用的命令,我们也可以看出常用的docker命令在docker-compose中也可使用。

你可能感兴趣的:(docker和docker-compose的前后端项目部署(含MySQL,Redis和RabbitMQ))