這是一次完整的項目實踐,Angular頁面+Springboot接口+MySQL都經過Dockerfile打包成docker鏡像,經過docker-compose作統一編排。目的是實現整個項目產品的輕量級和靈活性,在將各個模塊的鏡像都上傳公共鏡像倉庫後,任何人均可以經過 「docker-compose up -d」 一行命令,將整個項目的前端、後端、數據庫以及文件服務器等,運行在本身的服務器上。
本項目是開發一個相似於segmentfault的文章共享社區,個人設想是當部署在我的服務器上時就是我的的文章庫,部署在項目組的服務器上就是項目內部的文章庫,部署在公司的服務器上就是全部職工的文章共享社區。突出的特色就是,項目相關的全部應用和文件資源都是靈活的,用戶能夠傻瓜式地部署並使用,對宿主機沒有任何依賴。
目前一共有三個docker鏡像,考慮之後打在一個鏡像中,但目前只能經過docker-compose來編排這三個鏡像。javascript
最後三個docker容器的編排經過docker-compose來實現,三個容器之間的相互訪問都經過容器內部的別名,避免了宿主機遷移時ip沒法對應的問題。爲了方便開發,順便配了個自動部署。php
在項目完成後,須要生成項目所需數據庫、表結構以及基礎表數據的腳本,保證在運行該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腳本就生成好了。前端
咱們生成的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構建鏡像的方式不少,有經過代碼生成鏡像的,也有經過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上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; } }
一樣建立一個目錄,包含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 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;