一个网站的微服务架构实战(1)docker和 docker-compose

前言

这是一次完整的项目实践,Angular页面+Springboot接口+MySQL都经过Dockerfile打包成docker镜像,经过docker-compose作统一编排。目的是实现整个项目产品的轻量级和灵活性,在将各个模块的镜像都上传公共镜像仓库后,任何人均可以经过 “docker-compose up -d” 一行命令,将整个项目的前端、后端、数据库以及文件服务器等,运行在本身的服务器上。
本项目是开发一个相似于segmentfault的文章共享社区,个人设想是当部署在我的服务器上时就是我的的文章库,部署在项目组的服务器上就是项目内部的文章库,部署在公司的服务器上就是全部职工的文章共享社区。突出的特色就是,项目相关的全部应用和文件资源都是灵活的,用户能够傻瓜式地部署并使用,对宿主机没有任何依赖。
目前一共有三个docker镜像,考虑之后打在一个镜像中,但目前只能经过docker-compose来编排这三个镜像。javascript

  1. MySQL镜像:以MySQL为基础,将项目所用到的数据库、表结构以及一些基础表的数据库,经过SQL脚本打包在镜像中。用户在启动镜像后就自动建立了项目全部的数据库、表和基础表数据。
  2. SpringBoot镜像:后台接口经过SpringBoot开发,开发完成后直接能够打成镜像,因为其内置tomcat,能够直接运行,数据库指向启动好的MySQL容器中的数据库。
  3. Nginx(Angular)镜像:Nginx镜像中打包了Angular项目的dist目录资源,以及default.conf文件。主要的做用有:部署Angular项目页面;挂载宿主机目录做为文件服务器;以及反向代理SpringBoot接口,解决跨域问题等等。

最后三个docker容器的编排经过docker-compose来实现,三个容器之间的相互访问都经过容器内部的别名,避免了宿主机迁移时ip没法对应的问题。为了方便开发,顺便配了个自动部署。php

MySQL镜像

初始化脚本

在项目完成后,须要生成项目所需数据库、表结构以及基础表数据的脚本,保证在运行该docker容器中,启动MySQL数据库时,自动构建数据库和表结构,并初始化基础表数据。
Navicat for MySQL的客户端支持导出数据库的表结构和表数据的SQL脚本。
若是没有安装Navicat,能够在链接上容器中开发用的MySQL数据库,经过mysqldump 命令导出数据库表结构和数据的SQL脚本。下文中就是将数据库的SQL脚本导出到宿主机的/bees/sql 目录:css

docker exec -it  mysql mysqldump -uroot -pPASSWORD 数据库名称 > /bees/sql/数据库名称.sql

以上只是导出 表结构和表数据的脚本,还要在SQL脚本最上方加上 生成数据库的SQL:html

drop database if exists 数据库名称;
create database 数据库名称;
use 数据库名称;

经过以上两个步骤,数据库、表结构和表数据三者的初始化SQL脚本就生成好了。前端

Dockerfile构建镜像

咱们生成的SQL脚本叫 bees.sql,在MySQL官方镜像中提供了容器启动时自动执行/docker-entrypoint-initdb.d文件夹下的脚本的功能(包括shell脚本和sql脚本),咱们在后续生成镜像的时候,将上述生成的SQL脚本COPY到MySQL的/docker-entrypoint-initdb.d目录下就能够了。
如今咱们写Dockerfile,很简单:java

FROM mysql

MAINTAINER kerry(kerry.wu@definesys.com)

COPY bees.sql /docker-entrypoint-initdb.d

将 bees.sql 和 Dockerfile 两个文件放在同一目录,执行构建镜像的命令就能够了:mysql

docker build -t bees-mysql .

如今经过 docker images,就能看到本地的镜像库中就已经新建了一个 bees-mysql的镜像啦。nginx

SpringBoot镜像

springboot构建镜像的方式不少,有经过代码生成镜像的,也有经过jar包生成镜像的。我不想对代码有任何污染,就选择后者,经过生成的jar包构建镜像。
建立一个目录,上传已经准备好的springboot的jar包,这里名为bees-0.0.1-SNAPSHOT.jar,而后一样编写Dockerfile文件:git

FROM java:8
VOLUME /tmp
ADD bees-0.0.1-SNAPSHOT.jar /bees-springboot.jar
EXPOSE 8010
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","-Denv=DEV","/bees-springboot.jar"]

将bees-0.0.1-SNAPSHOT.jar和Dockerfile放在同一目录执行命令开始构建镜像,一样在本地镜像库中就生成了bees-springboot的镜像:github

docker build -t bees-springboot .

Nginx(Angular)镜像

Nginx的配置

该镜像主要在于nginx上conf.d/default.conf文件的配置,主要实现三个需求:

一、Angualr部署
Angular的部署很简单,只要将Angular项目经过 ng build --prod 命令生成dist目录,将dist目录做为静态资源文件放在服务器上访问就行。咱们这里就把dist目录打包在nginx容器中,在default.conf上配置访问。
二、文件服务器
项目为文章共享社区,少不了的就是一个存储文章的文件服务器,包括存储一些图片之类的静态资源。须要在容器中建立一个文件目录,经过default.conf上的配置将该目录代理出来,能够直接访问目录中的文件。
固然为了避免丢失,这些文件最好是保存在宿主机上,在启动容器时能够将宿主机本地的目录挂载到容器中的文件目录。
三、接口跨域问题
在先后端分离开发的项目中,“跨域问题”是较为常见的,SpringBoot的容器和Angular所在的容器不在同一个ip和端口,咱们一样能够在default.conf上配置反向代理,将后台接口代理成同一个ip和端口的地址。

话很少说,结合上面三个问题,咱们最终的default.conf为:

server {
    listen       80;

    server_name  localhost;
    
    gzip on;
    gzip_min_length  1k;
    gzip_buffers     4 16k;
    gzip_comp_level 3;
    gzip_types       text/plain application/x-javascript application/javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
    gzip_vary on;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
        try_files $uri $uri/ /index.html;
    }
    
    location /api/ {
        proxy_pass http://beesSpringboot:8010/;
    }

    location /file {
        alias /home/file;
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}
  1. location / :代理的是Angular项目,dist目录内经过Dockerfile
    COPY在容器内的/usr/share/nginx/html目录;
  2. location /file :代理/home/file 目录,做为文件服务器;
  3. location /api/ :是为了解决跨域而作的反向代理,为了脱离宿主机的限制,接口所在容器的ip经过别名beesSpringboot来代替。别名的设置是在docker-compose.yml中设置的,后续再讲。
  4. 添加了gzip,针对前端较大资源文件下载速度的优化

Dockerfile构建镜像

一样建立一个目录,包含Angualr的dist目录、Dockerfile和nginx的default.conf文件,目录结构以下:

[root@Kerry angular]# tree
.
├── dist
│   └── Bees
│       ├── 0.cb202cb30edaa3c93602.js
│       ├── 1.3ac3c111a5945a7fdac6.js
│       ├── 2.99bfc194c4daea8390b3.js
│       ├── 3.50547336e0234937eb51.js
│       ├── 3rdpartylicenses.txt
│       ├── 4.53141e3db614f9aa6fe0.js
│       ├── assets
│       │   └── images
│       │       ├── login_background.jpg
│       │       └── logo.png
│       ├── favicon.ico
│       ├── index.html
│       ├── login_background.7eaf4f9ce82855adb045.jpg
│       ├── main.894e80999bf907c5627b.js
│       ├── polyfills.6960d5ea49e64403a1af.js
│       ├── runtime.37fed2633286b6e47576.js
│       └── styles.9e4729a9c6b60618a6c6.css
├── Dockerfile
└── nginx
    └── default.conf

Dockerfile文件以下:

FROM nginx

COPY nginx/default.conf /etc/nginx/conf.d/

RUN rm -rf /usr/share/nginx/html/*

COPY /dist/Bees /usr/share/nginx/html

CMD ["nginx", "-g", "daemon off;"]

以上,经过下列命令,构建bees-nginx-angular的镜像完成:

docker build -t bees-nginx-angular .

docker-compose容器服务编排

上述,咱们已经构建了三个镜像,相对应的至少要启动三个容器来完成项目的运行。那要执行三个docker run?太麻烦了,并且这三个容器之间还须要相互通讯,若是只使用docker来作的话,不光启动容器的命令会很长,并且为了容器之间的通讯,docker --link 都会十分复杂,这里咱们须要一个服务编排。docker的编排名气最大的固然是kubernetes,但个人初衷是让这个项目轻量级,不太但愿用户安装偏重量级的kubernetes才能运行,而我暂时又没能解决将三个镜像构建成一个镜像的技术问题,就选择了适中的一个产品--docker-compse。
安装docker-compose很简单,这里就不赘言了。安装完以后,随便找个目录,写一个docker-compose.yml文件,而后在该文件所在地方执行一行命令就能将三个容器启动了:

#启动
docker-compose up -d
#关闭
docker-compose down

这里直接上我写的docker-compose.yml文件

version: "2"
services:

 beesMysql:
  restart: always
  image: bees-mysql
  ports:
   - 3306:3306
  volumes:
   - /bees/docker_volume/mysql/conf:/etc/mysql/conf.d
   - /bees/docker_volume/mysql/logs:/logs
   - /bees/docker_volume/mysql/data:/var/lib/mysql
  environment:
   MYSQL_ROOT_PASSWORD: kerry

 beesSpringboot:
  restart: always
  image: bees-springboot
  ports:
   - 8010:8010
  depends_on:
   - beesMysql

 beesNginxAngular:
  restart: always
  image: bees-nginx-angular
  ports:
   - 8000:80
  depends_on:
   - beesSpringboot
  volumes:
   - /bees/docker_volume/nginx/nginx.conf:/etc/nginx/nginx.conf
   - /bees/docker_volume/nginx/conf.d:/etc/nginx/conf.d
   - /bees/docker_volume/nginx/file:/home/file

image:镜像名称
ports:容器的端口和宿主机的端口的映射
services:文中三个service,在各自容器启动后就会自动生成别名,例如:在springboot中访问数据库,只须要经过“beesMysql:3306”就能访问。
depends_on:会设置被依赖的容器启动以后,才会启动本身。例如:mysql数据库容器启动后,再启动springboot接口的容器。
volumes:挂载卷,一些须要长久保存的文件,可经过宿主机中的目录,挂载到容器中,不然容器重启后会丢失。例如:数据库的数据文件;nginx的配置文件和文件服务器目录。

其余

自动部署

为了提升开发效率,简单写了一个自动部署的脚本,直接贴脚本了:

#!/bin/bash

v_springboot_jar=`find /bees/devops/upload/ -name "*.jar"`
echo "找到jar:"$v_springboot_jar
v_angular_zip=`find /bees/devops/upload/ -name "dist.zip"`
echo "找到dist:"$v_angular_zip

cd /bees/conf/
docker-compose down
echo "关闭容器"

docker rmi -f $(docker images |  grep "bees-springboot"  | awk '{print $1}')
docker rmi -f $(docker images |  grep "bees-nginx-angular"  | awk '{print $1}')
echo "删除镜像"

cd /bees/devops/dockerfiles/springboot/
rm -f *.jar
cp $v_springboot_jar ./bees-0.0.1-SNAPSHOT.jar
docker build -t bees-springboot .
echo "生成springboot镜像"

cd /bees/devops/dockerfiles/angular/
rm -rf dist/
cp $v_angular_zip ./dist.zip
unzip dist.zip
rm -f dist.zip
docker build -t bees-nginx-angular .
echo "生成angular镜像"

cd /bees/conf/
docker-compose up -d
echo "启动容器"
docker ps |grep bees

遇到的坑

一开始在docker-compose.yml文件中写services时,每一个service不是驼峰式命名,而是下划线链接,例如:bees_springboot、bees_mysql、bees_nginx_angular 。
在springboot中访问数据库的别名能够,可是在nginx中,反向代理springboot接口地址时死活代理不了 bees_springboot的别名。能在bees_nginx_angular的容器中ping通bees_springboot,可是代理不了bees_springboot地址的接口,经过curl -v 查看缘由,是丢失了host。
最后发现,nginx默认request的header中包含“_”下划线时,会自动忽略掉。我所以把docker-compose.yml中service名称,从下划线命名都改为了驼峰式。
固然也能够经过在nginx里的nginx.conf配置文件中的http部分中添加以下配置解决:

underscores_in_headers on;

备注

一、线上网站地址:纸蜂平台
二、 github地址,欢迎star

相关文章
相关标签/搜索