将你的前端应用打包成docker镜像并部署到服务器?仅需一个脚本搞定

1.前言

前段时间,本身搞了个阿里云的服务器。想本身在上面折腾,可是不想由于本身瞎折腾而污染了现有的环境。毕竟,如今的阿里云已经没有免费的快照服务了。要想还原的话,最简单的办法就是从新装系统。而一旦重装,以前的搭建的全部环境就都白搭了。html

再加上以前自己就想引入docker,因此就打算利用docker容器来部署此次的前端应用。前端

2.构建前端应用

在打包以前,首先须要一个可正常运行的前端应用。这个可使用umi或者create-react-app来构建。react

3.nginx的默认配置文件

而后须要在项目中加上默认nginx配置文件。nginx

server {
    listen 80;
    server_name localhost;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
        try_files $uri $uri/ /index.html;
    }
}

4.编写本地构建脚本

4.1. 移除上次的目录和Dockerfile

#!/bin/bash

if [ -d "./dist" ]; then
    rm -rf ./dist
fi

if [ -f "./Dockerfile" ]; then
    rm -f ./Dockerfile
fi

由于每次更改后dist中的内容确定与以前不一样,其实这一步显得不是那么必要。运行npm的打包命令也会自动清楚该目录。git

而清除Dockerfile则是为了防止更新了Dockerfile,而此次却不能获得最新的配置。github

4.2. 打包前端应用

执行前端的打包命令,生成静态文件目录。docker

yarn build

4.3. 生成Dockerfile

echo "FROM nginx:latest" >> ./Dockerfile
echo "COPY ./dist /usr/share/nginx/html/" >> ./Dockerfile
echo "COPY ./default.conf /etc/nginx/conf.d/" >> ./Dockerfile
echo "EXPOSE 80" >> ./Dockerfile

FROM制定了该定制容器的基础镜像为nginx:latest;COPY命里将打包好的静态文件目录复制到容器内的/usr/share/nginx/html/目录下,而后将nginx的配置写入容器中对应的位置; EXPOSE则是设置对外暴露容器的80端口。npm

4.4. 生成并推送定制image

docker build -t detectivehlh/mine .
docker login -u detectivehlh -p ********
docker push detectivehlh/mine

这里是在开发本地,使用docker命令来打包,因此该脚本对docker有强依赖。build命令表示打包docker应用的,-t选项则制定了docker镜像的名字和tag,tag会默认为latest。bash

而后登陆dockerHub,将定制好的镜像推送到dockerHub中。detectivehlh就是dockerHub的用户名,mine是image的名字。服务器

4.5. 删除tag为none的无用image

第一次构建不会生成tag为none的image,可是后面每次再次执行该命令就会出现这样的状况。因此每次构建了一个新的image后,须要清除调不须要的image。

docker images | grep none | awk '{print $3}' | xargs docker rmi

使用grep命令匹配到tag为none的image,awk是一个强大的文本分析工具,{print $3}表示打印出匹配到的每一行的第三个字段,也就是docker的image id。若是是$0的话表示当前整行的数据。

xargs是一个给其余命令(也就是后面的docker rmi)传递参数的一个过滤器,将标准输入转换成命令行参数。

总结来讲,上述命令就是找到tag为none的image的ID,而后使用docker rmi命令移除该image。

4.6. 执行部署

cmd="cd ~ && sh deploy.sh mine"
ssh -t USER_NAME@IP_ADDRESS "bash -c \"${cmd}\""

经过ssh命令,登陆远程服务器,而且执行参数中的脚本。

deploy.sh是放在服务端的构建脚本。放在默认的登陆用户下。咱们发现,后面还跟了个mine,这是在服务器上运行的docker镜像的名字。这里暂时没有对container的名字加上hash,由于本身的小项目,暂时没有必要。

在项目中的完整构建脚本以下。

#!/bin/bash

if [ -d "./dist" ]; then
    rm -rf ./dist
fi
if [ -f "./Dockerfile" ]; then
    rm -f ./Dockerfile
fi

yarn build

echo "FROM nginx:latest" >> ./Dockerfile
echo "COPY ./dist /usr/share/nginx/html/" >> ./Dockerfile
echo "COPY ./default.conf /etc/nginx/conf.d/" >> ./Dockerfile
echo "EXPOSE 80" >> ./Dockerfile

docker build -t detectivehlh/mine .
docker login -u detectivehlh -p ********
docker push detectivehlh/mine

docker images | grep none | awk '{print $3}' | xargs docker rmi

cmd="cd ~ && sh deploy.sh mine"
ssh -t USER_NAME@IP_ADDRESS "bash -c \"${cmd}\""

5. 编写服务器部署脚本

从上面步骤来看,咱们还须要一个服务器端的部署脚本。你们可能会说,标题不是说一个脚本搞定吗?em。。。服务器一个,本地一个...简称只需一个脚本。

5.1 接收参数

在本地的构建脚本中,咱们传入了docker运行的container的名字。在服务器构建脚本中须要来接收它。而后更新刚刚推送的docker image。

#!/bin/bash
name=$1
docker pull detectivehlh/$name

5.2. 启动container

在启动container时咱们会面对两种状况,名字为传入参数的container已经在运行了。而在此时若是再次运行docker run命令就会报错而致使咱们没法使用最新的container,也没法达到更新应用的目的。

if docker ps | grep $name | awk {'print $(NF)'} | grep -Fx $name; then
    echo "Container mine is already start"
    docker stop $name
    docker rm $name
    docker run -d --name $name -p 3000:80 detectivehlh/$name
else
    echo "Container mine is not start!, starting"
    docker run -d --name $name -p 3000:80 detectivehlh/$name
    echo "Finish starting"
fi
docker images | grep none | awk '{print $3}' | xargs docker rmi

因此在这里作一个判断,第一个if判断若是存在名字为传入参数的container正在运行,就中止当前容器再从新启动。若是不存在则直接启动容器。

run命令就不过多解释了。-d表示后台运行容器并返回容器ID,--name表示设置容器的名字,-p表示设置端口,将阿里云服务器的3000端口映射到容器的80端口,最后一句表示要启动哪一个image(好像仍是解释了一遍)。

最后一句就是移除屡次更新后出现的tag为none的无用镜像。完整的脚本以下。

#!/bin/bash
name=$1
docker pull detectivehlh/$name
if docker ps | grep $name | awk {'print $(NF)'} | grep -Fx $name; then
    echo "Container mine is already start"
    docker stop $name
    docker rm $name
    docker run -d --name $name -p 3000:80 detectivehlh/$name
else
    echo "Container mine is not start!, starting"
    docker run -d --name $name -p 3000:80 detectivehlh/$name
    echo "Finish starting"
fi
docker images | grep none | awk '{print $3}' | xargs docker rmi

6. 若是你只是想打个包

看到标题进来的兄dei,若是只是想打包一个docker镜像,那么你只须要Dockerfile文件和docker build命令就OK了。

7. 总结

最初写这个脚本,主要目的是为了方便。因此脚本中为了达到这个目的作了一些调整。最终我达成了知足我需求的一个方便的部署脚本。

它的方便体如今,当我完成了项目代码的更新,只须要跑一下这个脚本,而后等待一下子,项目就会自动打包成docker image,而且自动的在个人服务器上运行该container。

可是这种方式会给实际的生产环境带来一些不可控的问题。好比,脚本必须不能上传,由于涉及一些服务器的敏感信息。可是若是你不当心上传了,那你的服务器就至关于裸奔了;再好比,你对你的代码必需要十分自信,没有通过测试的代码就直接部署,会带来一些风险。

若是是本身用的,那彻底不用担忧,想怎么搞怎么搞。可是若是是开放给全部人用的而且有必定的访问量,好比博客,那么对于其余用户来讲,这种方式就不怎么友好。

因此个人观点是,分状况来。目前来讲个人项目只有少数几我的在用,也还在处于迭代阶段。而且代码仓库是私有的,因此我彻底不用担忧隐私的问题。服务未经测试就直接上线对于我来讲,其实问题也不大。首先我会在本地测试,确认无误后才会执行部署操做。因此在不一样的阶段,找到最适合本身的方案就OK。

相关文章
相关标签/搜索