Angular(一) shell脚本实现一键部署angular docker镜像

简介: Angular(一) shell脚本实现一键部署angular docker镜像

一、背景



今天早上收到项目经理的一个任务,让我编写angular+ng-zorro-antd的一个前端项目的部署脚本。说实话,angular我也不是很会,就是来到项目组接手了一下。现在开发新的项目,项目搭建和部署脚本全归我了,臣妾做不到好不好!没办法,谁叫咋们是吃这碗饭的。


二、思路整理



我记得之前有配置过nginx的,它有正向代理,反向代理和动静分离等几大功能,于是乎干么不适用动静分离呢?


  • 在docker中拉取node:12-alpine镜像
  • npm i拉取依赖
  • npm run build打包
  • 移除nginx本身自带的/usr/share/nginx/html/*文件
  • 将打包好的文件复制到这个文件夹
  • 执行nginx
  • 镜像打包完成
  • 运行镜像,映射端口


三、代码实践



1. 编写nginx配置文件nginx.conf


server {
  listen 80;
  sendfile on;
  default_type application/octet-stream;
  gzip on;
  gzip_http_version 1.1;
  gzip_disable      "MSIE [1-6]\.";
  gzip_min_length   1100;
  gzip_vary         on;
  gzip_proxied      expired no-cache no-store private auth;
  gzip_types        text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
  gzip_comp_level   9;
  root /usr/share/nginx/html;
  location / {
    try_files $uri $uri/ /index.html =404;
  }
}


2. 编写Dockerfile构建镜像


### STAGE 1: Build ###
FROM node:12-alpine AS builder
# build-time variables 
# prod|release its value will be come from outside 
ARG env=prod
WORKDIR /yinghe
COPY . .
RUN npm config set registry https://registry.npm.taobao.org
RUN npm i
RUN npm run build:$env
### STAGE 2: Setup ###
FROM nginx:stable-alpine
## Copy our default nginx config
COPY nginx.conf /etc/nginx/conf.d/default.conf
## Remove default nginx website
RUN rm -rf /usr/share/nginx/html/*
## From ‘builder’ stage copy over the artifacts in dist folder to default nginx public folder
COPY --from=builder /yinghe/dist/yinghe-frontend /usr/share/nginx/html
CMD ["nginx", "-g", "daemon off;"]


3. 编写镜像和容器构建脚本deploy_admin


if [ $# -eq 0 ]
  then
    echo "Missing environment target"
    echo "FORMAT: ./deploy_admin ENV(dev|release|prod|staging|simulate)"
    exit 0
  else
    docker build --no-cache --build-arg env=$2 -t yinghe-frontend:$2 .
    docker stop yinghe-frontend
    docker rm yinghe-frontend
    docker run -d --name yinghe-frontend -p 80:$1 yinghe-frontend:$2
fi


4. 不同环境配置文件编写


640.png


5. 在package.json中添加构建scripts


640.png


添加不同环境的构建命令,指向不同的配置文件!方便对应不同环境的后端服务器。


6. 在angular.json中添加不同环境的配置文件指向


640.png


这里4和5应该对换一下位置的,奈何太晚了,有点懒得CV了。


7. 编写linux脚本拉取代码和执行部署脚本my-ssh-script.sh


echo "start deployment" $1 "..."
rm -rf yinghe-frontend
git clone -b $1 --single-branch https://用户别名:密码@gitee.com/yinghe-frontend.git
cd yinghe-frontend
chmod 777 deploy_admin
./deploy_admin $2 $3


四、主角docker安装



curl -fsSL https://get.docker.com/ | sh
systemctl restart docker
systemctl enable docker


作为本场的主角,必须着重讲一下,有些版本不支持FROM node:12-alpine AS builder,需要更新docker版本,更新用以上语句,怎么卸载就不写了,自己去查。不然会出现如下错误:


Error parsing reference: "node:12-alpine AS binarybuilder" is not a valid repository/tag: inval


五、服务器上部署angular服务



  1. 创建yinghe文件夹,授权,执行脚本
$ mkdir yinghe
$ cd yinghe
$ vim my-ssh-script.sh #里面添加刚才3.7里面的linux命令
$ chmod 777 my-ssh-script.sh
$ ./my-ssh-script.sh master 80 dev
# 第一个参数是远程分支名即master,第二个参数是端口即80,第三个参数是环境即dev


  1. 执行日志展示
start deployment master ...
Cloning into 'yinghe-frontend'...
remote: Enumerating objects: 99, done.
remote: Counting objects: 100% (99/99), done.
remote: Compressing objects: 100% (92/92), done.
remote: Total 99 (delta 23), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (99/99), done.
invalid argument "yinghe-frontend:" for "-t, --tag" flag: invalid reference format
See 'docker build --help'.
Error response from daemon: No such container: yinghe-frontend
Error: No such container: yinghe-frontend
docker: Invalid containerPort: dev.
See 'docker run --help'.
root@iZbp1g1qpfdlqea4avz2sfZ:~/yinghe# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
root@iZbp1g1qpfdlqea4avz2sfZ:~/yinghe# docker ps -a
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
root@iZbp1g1qpfdlqea4avz2sfZ:~/yinghe# docker run --name yinghe-frontend -p 80:80 -d nginx:stable-alpine
0a9e764c7c8be34e7dbd6f3e7c152ff3f516f9c8ddd6c44c5f86ecc41dfd5f17
root@iZbp1g1qpfdlqea4avz2sfZ:~/yinghe# docker ps
CONTAINER ID   IMAGE                 COMMAND                  CREATED         STATUS         PORTS                NAMES
0a9e764c7c8b   nginx:stable-alpine   "/docker-entrypoint.…"   4 seconds ago   Up 3 seconds   0.0.0.0:80->80/tcp   yinghe-frontend
root@iZbp1g1qpfdlqea4avz2sfZ:~/yinghe# ./my-ssh-script.sh master 80 dev
start deployment master ...
Cloning into 'yinghe-frontend'...
remote: Enumerating objects: 99, done.
remote: Counting objects: 100% (99/99), done.
remote: Compressing objects: 100% (92/92), done.
remote: Total 99 (delta 23), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (99/99), done.
invalid argument "yinghe-frontend:" for "-t, --tag" flag: invalid reference format
See 'docker build --help'.
yinghe-frontend
yinghe-frontend
docker: Invalid containerPort: dev.
See 'docker run --help'.
root@iZbp1g1qpfdlqea4avz2sfZ:~/yinghe# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
root@iZbp1g1qpfdlqea4avz2sfZ:~/yinghe# docker run --name yinghe-frontend -p 80:80 -d nginx:stable-alpine
00dff7994ce8072b5a192cedf106e884412ca9e7fd9f1b92d23c0b767d7c85e2
root@iZbp1g1qpfdlqea4avz2sfZ:~/yinghe# docker ps
CONTAINER ID   IMAGE                 COMMAND                  CREATED          STATUS          PORTS                NAMES
00dff7994ce8   nginx:stable-alpine   "/docker-entrypoint.…"   14 seconds ago   Up 13 seconds   0.0.0.0:80->80/tcp   yinghe-frontend
root@iZbp1g1qpfdlqea4avz2sfZ:~/yinghe# docker commit 00dff7994ce8 yinghe-frontend:dev
sha256:3991b7159075a6ce158369acd96b4f81027ac4f6e6a2ec2814ade3d057c82401
root@iZbp1g1qpfdlqea4avz2sfZ:~/yinghe# docker ps
CONTAINER ID   IMAGE                 COMMAND                  CREATED          STATUS          PORTS                NAMES
00dff7994ce8   nginx:stable-alpine   "/docker-entrypoint.…"   55 seconds ago   Up 54 seconds   0.0.0.0:80->80/tcp   yinghe-frontend
root@iZbp1g1qpfdlqea4avz2sfZ:~/yinghe# docker ps -a
CONTAINER ID   IMAGE                 COMMAND                  CREATED              STATUS          PORTS                NAMES
00dff7994ce8   nginx:stable-alpine   "/docker-entrypoint.…"   About a minute ago   Up 58 seconds   0.0.0.0:80->80/tcp   yinghe-frontend
root@iZbp1g1qpfdlqea4avz2sfZ:~/yinghe# docker images
REPOSITORY        TAG             IMAGE ID       CREATED          SIZE
yinghe-frontend   dev             3991b7159075   13 seconds ago   21.9MB
nginx             stable-alpine   f2343e2e2507   5 weeks ago      21.9MB
root@iZbp1g1qpfdlqea4avz2sfZ:~/yinghe# ./my-ssh-script.sh master 80 dev
start deployment master ...
Cloning into 'yinghe-frontend'...
remote: Enumerating objects: 99, done.
remote: Counting objects: 100% (99/99), done.
remote: Compressing objects: 100% (92/92), done.
remote: Total 99 (delta 23), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (99/99), done.
invalid argument "yinghe-frontend:" for "-t, --tag" flag: invalid reference format
See 'docker build --help'.
yinghe-frontend
yinghe-frontend
docker: Invalid containerPort: dev.
See 'docker run --help'.
root@iZbp1g1qpfdlqea4avz2sfZ:~/yinghe# ./my-ssh-script.sh master 80 dev
start deployment master ...
Cloning into 'yinghe-frontend'...
remote: Enumerating objects: 99, done.
remote: Counting objects: 100% (99/99), done.
remote: Compressing objects: 100% (92/92), done.
remote: Total 99 (delta 23), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (99/99), done.
invalid argument "yinghe-frontend:" for "-t, --tag" flag: invalid reference format
See 'docker build --help'.
Error response from daemon: No such container: yinghe-frontend
Error: No such container: yinghe-frontend
docker: Invalid containerPort: dev.
See 'docker run --help'.
root@iZbp1g1qpfdlqea4avz2sfZ:~/yinghe# ./my-ssh-script.sh master 80 dev
start deployment master ...
Cloning into 'yinghe-frontend'...
remote: Enumerating objects: 102, done.
remote: Counting objects: 100% (102/102), done.
remote: Compressing objects: 100% (95/95), done.
remote: Total 102 (delta 25), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (102/102), 163.66 KiB | 877.00 KiB/s, done.
Resolving deltas: 100% (25/25), done.
Sending build context to Docker daemon  885.8kB
Step 1/12 : FROM node:12-alpine AS builder
12-alpine: Pulling from library/node
0a6724ff3fcd: Already exists 
5fd2bdfdbf4b: Pull complete 
80b224d472a8: Pull complete 
e21405c347ae: Pull complete 
Digest: sha256:41ae2b38e38e387517c59ca50498eac0537a2ff2316552c3df3b406d31414d78
Status: Downloaded newer image for node:12-alpine
 ---> 0206ff8a5f9e
Step 2/12 : ARG env=prod
 ---> Running in 970decac1eb8
Removing intermediate container 970decac1eb8
 ---> 398d6df8583d
Step 3/12 : WORKDIR /yinghe
 ---> Running in b23562039c6a
Removing intermediate container b23562039c6a
 ---> 1d6ca484cf4b
Step 4/12 : COPY . .
 ---> 2463139a973b
Step 5/12 : RUN npm config set registry https://registry.npm.taobao.org
 ---> Running in 353fd70991b3
Removing intermediate container 353fd70991b3
 ---> 18e29fd5b322
Step 6/12 : RUN npm i
 ---> Running in 2ea255e1bd0c
> core-js@3.6.4 postinstall /yinghe/node_modules/@angular-devkit/build-angular/node_modules/core-js
> node -e "try{require('./postinstall')}catch(e){}"
Thank you for using core-js ( https://github.com/zloirock/core-js ) for polyfilling JavaScript standard library!
The project needs your help! Please consider supporting of core-js on Open Collective or Patreon: 
> https://opencollective.com/core-js 
> https://www.patreon.com/zloirock 
Also, the author of core-js ( https://github.com/zloirock ) is looking for a good job -)
> core-js@2.6.12 postinstall /yinghe/node_modules/core-js
> node -e "try{require('./postinstall')}catch(e){}"
Thank you for using core-js ( https://github.com/zloirock/core-js ) for polyfilling JavaScript standard library!
The project needs your help! Please consider supporting of core-js on Open Collective or Patreon: 
> https://opencollective.com/core-js 
> https://www.patreon.com/zloirock 
Also, the author of core-js ( https://github.com/zloirock ) is looking for a good job -)
> @angular/cli@11.0.7 postinstall /yinghe/node_modules/@angular/cli
> node ./bin/postinstall/script.js
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.3.1 (node_modules/@angular/compiler-cli/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.3.1: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.3.1 (node_modules/watchpack/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.3.1: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.1.3 (node_modules/rollup/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.1.3: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.3.1 (node_modules/ng-packagr/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.3.1: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.13 (node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
added 1544 packages from 1093 contributors in 27.776s
82 packages are looking for funding
  run `npm fund` for details
Removing intermediate container 2ea255e1bd0c
 ---> ea60964e889d
Step 7/12 : RUN npm run build:$env
 ---> Running in 1525734c5875
> yinghe-frontend@0.0.0 build:dev /yinghe
> ng build --configuration=dev
Compiling @angular/animations : es2015 as esm2015
Compiling @angular/core : es2015 as esm2015
Compiling ng-zorro-antd/core/environments : es2015 as esm2015
Compiling @angular/cdk/keycodes : es2015 as esm2015
Compiling ng-zorro-antd/core/animation : es2015 as esm2015
Compiling ng-zorro-antd/core/color : es2015 as esm2015
Compiling @angular/common : es2015 as esm2015
Compiling ng-zorro-antd/core/types : es2015 as esm2015
Compiling @angular/animations/browser : es2015 as esm2015
Compiling ng-zorro-antd/core/logger : es2015 as esm2015
Compiling @angular/cdk/platform : es2015 as esm2015
Compiling @angular/cdk/bidi : es2015 as esm2015
Compiling ng-zorro-antd/core/util : es2015 as esm2015
Compiling @angular/platform-browser : es2015 as esm2015
Compiling @angular/common/http : es2015 as esm2015
Compiling ng-zorro-antd/core/polyfill : es2015 as esm2015
Compiling @angular/cdk/layout : es2015 as esm2015
Compiling ng-zorro-antd/core/outlet : es2015 as esm2015
Compiling @angular/platform-browser/animations : es2015 as esm2015
Compiling ng-zorro-antd/core/services : es2015 as esm2015
Compiling @ant-design/icons-angular : es2015 as esm2015
Compiling @angular/cdk/collections : es2015 as esm2015
Compiling @angular/cdk/portal : es2015 as esm2015
Compiling @angular/cdk/scrolling : es2015 as esm2015
Compiling ng-zorro-antd/core/config : es2015 as esm2015
Compiling @ant-design/icons-angular/icons : es2015 as esm2015
Compiling ng-zorro-antd/core/no-animation : es2015 as esm2015
Compiling @angular/cdk/overlay : es2015 as esm2015
Compiling @angular/forms : es2015 as esm2015
Compiling ng-zorro-antd/core/overlay : es2015 as esm2015
Compiling ng-zorro-antd/icon : es2015 as esm2015
Compiling ng-zorro-antd/core/time : es2015 as esm2015
Compiling @angular/cdk/observers : es2015 as esm2015
Compiling ng-zorro-antd/core/transition-patch : es2015 as esm2015
Compiling @angular/cdk/a11y : es2015 as esm2015
Compiling ng-zorro-antd/core/wave : es2015 as esm2015
Compiling ng-zorro-antd/i18n : es2015 as esm2015
Compiling ng-zorro-antd/button : es2015 as esm2015
Compiling @angular/router : es2015 as esm2015
Compiling ng-zorro-antd/core/resize-observers : es2015 as esm2015
Compiling ng-zorro-antd/tooltip : es2015 as esm2015
Compiling ng-zorro-antd/empty : es2015 as esm2015
Compiling ng-zorro-antd/menu : es2015 as esm2015
Compiling ng-zorro-antd/pipes : es2015 as esm2015
Compiling ng-zorro-antd/checkbox : es2015 as esm2015
Compiling ng-zorro-antd/select : es2015 as esm2015
Compiling ng-zorro-antd/dropdown : es2015 as esm2015
Compiling ng-zorro-antd/radio : es2015 as esm2015
Compiling ng-zorro-antd/spin : es2015 as esm2015
Compiling ng-zorro-antd/time-picker : es2015 as esm2015
Compiling ng-zorro-antd/grid : es2015 as esm2015
Compiling ng-zorro-antd/core/highlight : es2015 as esm2015
Compiling ng-zorro-antd/input : es2015 as esm2015
Compiling ng-zorro-antd/pagination : es2015 as esm2015
Compiling ng-zorro-antd/core/pipe : es2015 as esm2015
Compiling @angular/platform-browser-dynamic : es2015 as esm2015
Compiling ng-zorro-antd/modal : es2015 as esm2015
Compiling ng-zorro-antd/tabs : es2015 as esm2015
Compiling ng-zorro-antd/table : es2015 as esm2015
Compiling ng-zorro-antd/date-picker : es2015 as esm2015
Compiling ng-zorro-antd/form : es2015 as esm2015
Compiling ng-zorro-antd/divider : es2015 as esm2015
Compiling ng-zorro-antd/layout : es2015 as esm2015
Compiling ng-zorro-antd/cascader : es2015 as esm2015
Compiling ng-zorro-antd/switch : es2015 as esm2015
Compiling ng-zorro-antd/popconfirm : es2015 as esm2015
Compiling ng-zorro-antd/page-header : es2015 as esm2015
Compiling ng-zorro-antd/skeleton : es2015 as esm2015
Compiling ng-zorro-antd/progress : es2015 as esm2015
Compiling ng-zorro-antd/collapse : es2015 as esm2015
Compiling ng-zorro-antd/tag : es2015 as esm2015
Compiling ng-zorro-antd/result : es2015 as esm2015
Compiling ng-zorro-antd/space : es2015 as esm2015
Compiling ng-zorro-antd/alert : es2015 as esm2015
Compiling ng-zorro-antd/statistic : es2015 as esm2015
Compiling ng-zorro-antd/breadcrumb : es2015 as esm2015
chunk {} runtime.9fb644ab68599d00176c.js (runtime) 2.23 kB [entry] [rendered]
chunk {1} main.006e2e0096b60be5d8ae.js (main) 751 kB [initial] [rendered]
chunk {2} polyfills.7aa99d0d2d925cfabce1.js (polyfills) 44 kB [initial] [rendered]
chunk {3} polyfills-es5.3c2f87ef4cba455906e0.js (polyfills-es5) 127 kB [initial] [rendered]
chunk {4} styles.7f7c35f1bc66195ddfd2.css (styles) 676 kB [initial] [rendered]
chunk {5} 5.a87c4523ab86b1c2ce57.js () 58.9 kB  [rendered]
Date: 2021-01-25T13:05:59.082Z - Hash: 762297bdde32f3cf1aa6 - Time: 69919ms
Removing intermediate container 1525734c5875
 ---> 14b842766787
Step 8/12 : FROM nginx:stable-alpine
 ---> f2343e2e2507
Step 9/12 : COPY nginx.conf /etc/nginx/conf.d/default.conf
 ---> d440ea90fe9e
Step 10/12 : RUN rm -rf /usr/share/nginx/html/*
 ---> Running in 07fa1bb4c90c
Removing intermediate container 07fa1bb4c90c
 ---> 967a2883eaf8
Step 11/12 : COPY --from=builder /yinghe/dist/yinghe-frontend /usr/share/nginx/html
 ---> fdffaae10f7f
Step 12/12 : CMD ["nginx", "-g", "daemon off;"]
 ---> Running in 8c4caa8629b4
Removing intermediate container 8c4caa8629b4
 ---> d8da48b08a1b
Successfully built d8da48b08a1b
Successfully tagged yinghe-frontend:dev


  1. docker 容器和镜像查看
root@iZbp1g1qpfdlqea4avz2sfZ:~/yinghe# docker ps
CONTAINER ID   IMAGE                 COMMAND                  CREATED       STATUS       PORTS                NAMES
7244459f21cd   yinghe-frontend:dev   "/docker-entrypoint.…"   4 hours ago   Up 4 hours   0.0.0.0:80->80/tcp   yinghe-frontend
root@iZbp1g1qpfdlqea4avz2sfZ:~/yinghe# docker images
REPOSITORY        TAG             IMAGE ID       CREATED       SIZE
yinghe-frontend   dev             d8da48b08a1b   4 hours ago   24.8MB
node              12-alpine       0206ff8a5f9e   2 weeks ago   88.9MB
nginx             stable-alpine   f2343e2e2507   5 weeks ago   21.9MB


  1. 页面访问

640.png


  1. 至此部署成功,欢迎采纳!!!


目录
相关文章
|
21小时前
|
存储 监控 Serverless
Serverless应用引擎(SAE)不支持直接通过Docker Compose进行部署
【2月更文挑战第8天】Serverless应用引擎(SAE)不支持直接通过Docker Compose进行部署
6 1
|
2天前
|
搜索推荐 测试技术 数据安全/隐私保护
【Docker项目实战】使用Docker部署Fenrus个人仪表盘
【2月更文挑战第7天】使用Docker部署Fenrus个人仪表盘
12 0
|
2天前
|
JSON 测试技术 Linux
【Docker项目实战】使用Docker部署TeamMapper思维导图工具
【2月更文挑战第6天】使用Docker部署TeamMapper思维导图工具
16 1
|
3天前
|
缓存 监控 测试技术
【Docker项目实战】使用Docker部署miniboard轻量级监控仪表板
【2月更文挑战第5天】使用Docker部署miniboard轻量级监控仪表板
49 0
|
5天前
|
Java Shell Perl
使用shell脚本给日志文件瘦身
使用shell脚本给日志文件瘦身
|
5天前
|
关系型数据库 MySQL Docker
在win10安装docker及部署mysql5.6过程
在win10安装docker及部署mysql5.6过程
|
5天前
|
IDE Cloud Native 开发工具
云原生之在Docker环境下部署Atheos云IDE平台
【2月更文挑战第3天】云原生之在Docker环境下部署Atheos云IDE平台
231 0
|
5天前
|
安全 应用服务中间件 开发工具
Ubuntu20安装docker并部署相关漏洞环境
Ubuntu20安装docker并部署相关漏洞环境
19 0
|
6天前
|
测试技术 Linux 数据安全/隐私保护
【好用的个人工具】在Docker环境下部署WatchYourLAN轻量级网络IP扫描器
【2月更文挑战第2天】在Docker环境下部署WatchYourLAN轻量级网络IP扫描器
42 0
|
6天前
|
Cloud Native Go 数据安全/隐私保护
自定义Docker镜像推送到Docker Hub实战
自定义Docker镜像推送到Docker Hub实战
33 2
自定义Docker镜像推送到Docker Hub实战

相关产品

  • 云迁移中心