前端项目的Docker镜像打包

对读者的要求

  • 掌握nginx基本用法
  • 掌握Docker基础用法
  • Linux 命令行基本操作(Windows下则是掌握git bash这个工具)

简介

所有的项目最终都要布署到线上才能对外提供服务,在布署方案上,之前主要采用git拉取的方式,而现在则主要使用docker直接启动镜像的方式,或者通过云容器k8saws ecs启动镜像。不论哪一种方式,项目的Docker镜像制作都是必要的。后端为主的项目和纯前端的Docker镜像制作流程是一样的,但是在配置上后端会复杂一些。因此分开讨论,本文只谈前端项目的镜像制作。

为方便讨论,本文所指的前端项目,本文使用umijs的脚手架创建的前端项目,用于演示下面的步骤。读者使用相同的脚本架基本上直接拷贝代码就能使用。

最后达到的效果应该是用户在任何一台机器上,直接执行下面的命令,启动镜像就能完成布署:

docker run -p 8080:80 pheye/demo-app:latest

步骤

创建镜像文件

在前端工程目录下,创建Dockerfile文件

FROM nginx:alpine
MAINTAINER LIUWENCAN <phenye@gmail.com>
RUN adduser -D -H -u 5000 -s /bin/sh www
RUN rm /etc/nginx/conf.d/default.conf
ADD scripts/nginx.conf /etc/nginx/
ADD scripts/app.conf /etc/nginx/sites-available/
ADD dist /var/www
VOLUME /var/www
CMD ["nginx"]

这里面会将dist目录、scripts/nginx.confscripts/app.conf这2个nginx配置文件添加到镜像中。

create-react-app默认输出的目录叫build,请改为dist目录

其中,dist目录是通过npm run build生成的。而scripts/nginx.conf是一个全局的nginx,是很固定的,主要是做打开gzip压缩、设置日志格式等等,与本教程关系不大,因此直接查看附录将文件拷贝过去即可。

scripts/app.conf才是应用的配置文件,直接见下面配置,特别需要注意的是/api这一节,它对应到前端在调试时使用的反向代理配置,如果不需要可直接忽略。

server {
    listen 80;

    root /var/www;

    location ~* .*\.(gif|jpg|jpeg|png|bmp|swf|js|css)$ {
      expires 30d;
    }

    location / {
        # 用于配合 browserHistory使用
        try_files $uri $uri/ /index.html;
    }

    location /api {
        proxy_pass http://172.16.3.100:9200;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_set_header   X-Real-IP        $remote_addr;
    }

}

构建镜像

docker build -t demo-app:latest .

测试镜像

docker run -p 80:80 demo-app:latest

正常启动后,在浏览器中输入http://127.0.0.1/确认下镜像是否正常工作。

推送镜像

完成测试以后,要将镜像推送到公共的镜像仓库,然后在服务器上再拉取下来。一般来说,如果是可公开的,推送到Docker Hub就完事了。

docker tag demo-app:latest pheye/demo-app:latest
docker push pheye/demo-app:latest

但是在公司里面,一般都是要推送到私有镜像仓库,可选择阿里云镜像仓库、AWS ECR,或者在内网自建镜像仓库都行。本文以阿里云为例演示推送

# 登陆阿里云镜像仓库
username=${ALIYUN_REGISTRY_USERNAME}
pwd=${ALIYUN_REGISTRY_PASSWORD}
docker login --username=$username -p $pwd registry.cn-hangzhou.aliyuncs.com

# 修改TAG并推送
docker tag demo-app:latest registry.cn-hangzhou.aliyuncs.com/phenye/demo-app:latest
docker push registry.cn-hangzhou.aliyuncs.com/phenye/demo-app:latest

推送镜像以后,再登陆其他的机器VPS,直接按照“简介”中的方式,启动镜像应该能正常访问。

优化流程

前面的示例,总是打包成demo-app:latest,版本号总是latest,这种方式在生产方式下一旦出问题是没办法回滚的。实际使用场景通常是跟CI/CD结合,以每个git commit作为版本号,有问题的时候就回滚。因此构建和推送都要支持版本号,每次都手动敲命令很麻烦。可以增加两个脚本优化这个流程:

优化构建

创建scripts/build.sh文件:

#!/bin/sh

if [ $# -gt 1 ] ; then
docker build -t demo-app:$1 -t  demo-app:latest  .
else
docker build -t  demo-app:latest  .
fi

构建镜像,要指定版本时,就执行如下命令:

./scripts/build.sh v1.0.0

优化推送

创建./scripts/push.sh

#!/bin/sh

pwd=${ALIYUN_REGISTRY_PASSWORD}
docker login --username=phenye -p $pwd registry.cn-hangzhou.aliyuncs.com
docker tag demo-app:latest registry.cn-hangzhou.aliyuncs.com/phenye/demo-app:latest
docker push registry.cn-hangzhou.aliyuncs.com/phenye/demo-app:latest


if [ $# -gt 0 ] ; then
  tag=$1
  docker tag demo-app:latest registry.cn-hangzhou.aliyuncs.com/phenye/demo-app:${tag}
  docker push registry.cn-hangzhou.aliyuncs.com/phenye/demo-app:${tag}
fi

要在推送时指定版本号,就使用如下命令

./scripts/push.sh v1.0.0

附录

工程目录结构

为便于讨论,列出工程目录结构,方案在后续章节出现的文件,如果不知道它们应该放哪里,可参照这个工程目录结构。

.
├── Dockerfile
├── dist/
├── pages/
│   ├── index.css
│   └── index.js
└── scripts/
    ├── app.conf
    ├── build.sh
    ├── nginx.conf
    └── push.sh

nginx配置

nginx.conf并不是必须的,它主要做开启压缩、配置日志等操作,是一些全局的配置。

user www;
worker_processes 4;
pid /run/nginx.pid;
daemon off;

events {
  worker_connections  2048;
  multi_accept on;
  use epoll;
}

http {
  server_tokens off;
  sendfile on;
  tcp_nopush on;
  tcp_nodelay on;
  keepalive_timeout  600;
  fastcgi_read_timeout 300;
  types_hash_max_size 2048;
  client_max_body_size 20M;
  server_names_hash_bucket_size 256;
  include /etc/nginx/mime.types;
  default_type application/octet-stream;

  gzip on;
  gzip_disable "msie6";
  gzip_min_length 1k;
  gzip_comp_level 1;
  gzip_vary on;
  gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;

  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';

  log_format access  '$http_x_forwarded_for $remote_addr [$time_local] "http://$host" "$request" '
              '$status $body_bytes_sent "$http_referer" "$http_user_agent" "$remote_user" ';

  log_format main   '{"@timestamp":"$time_iso8601",'
                        '"@source":"$server_addr",'
                        '"hostname":"$hostname",'
                        '"ip":"$http_x_forwarded_for",'
                        '"client":"$remote_addr",'
                        '"request_method":"$request_method",'
                        '"scheme":"$scheme",'
                        '"domain":"$server_name",'
                        '"referer":"$http_referer",'
                        '"request":"$request_uri",'
                        '"args":"$args",'
                        '"size":$body_bytes_sent,'
                        '"status": $status,'
                        '"responsetime":$request_time,'
                        '"upstreamtime":"$upstream_response_time",'
                        '"upstreamaddr":"$upstream_addr",'
                        '"http_user_agent":"$http_user_agent",'
                        '"https":"$https"'
                        '}';
  access_log /dev/stdout main;
  error_log /dev/stderr;
  include /etc/nginx/conf.d/*.conf;
  include /etc/nginx/sites-available/*.conf;
  open_file_cache off; # Disabled for issue 619
  charset UTF-8;
}
posted @ 2020-05-07 17:30  枫叶落一地  阅读(10020)  评论(1编辑  收藏  举报