前端項目的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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM