docker

docker和docker-compose的前后端项目部署(含M

2019-02-13  本文已影响0人  dreamguys

前言

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是在操作系统层面上实现的虚拟化

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:命令详解

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 .

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

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中也可使用。

上一篇 下一篇

猜你喜欢

热点阅读