开发者如何快速精简容器云镜像?| 技术头条

戳蓝字“CSDN云计算”关注我们哦!


640?wx_fmt=png


作者:阿木


接触过容器云或者用过容器的同学一般都会遇到容器镜像占用空间很大的问题,遇到此类问题的时候大部分同学可能更加习惯于为容器的镜像仓库增加磁盘空间,当然这种方式无可厚非,毕竟这种方式可以帮助我们快速的解决掉手里的问题。

 

除了上面扩磁盘的解决方式,其实我们还可以采用缩减容器镜像的方式,此种方式不但可以帮助我们节省添加新磁盘的开支还可减少我们制作镜像和传输镜像的时间。优化的比较好的镜像占用的空间基本和应用的文件占用的空间相当,基本不会占用太多的额外存储空间。下面我们会具体看一下。 

 

640?wx_fmt=png

 

接触过Docker 的同学都知道Dockerfile 是一些指令的组成,且Dockerfile 文件中的每条指令对应着Linux 操作系统中的一条命令,当我们构建镜像时Docker 程序会将这些Dockerfile指令翻译成Linux可执行的命令。

 

Dockerfile 中每一条指令都会创建一个镜像层,随着指令的执行继而会增加镜像整体的大小。

 

Dockerfile 文件有自己的书写格式和支持的命令,常用Dockerfile 指令如下:

FROM  指定基镜像。

 

MAINTAINER  设置镜像的作者信息,如作者姓名、邮箱等。

 

COPY  将文件从本地复制到镜像,拷贝前需要保证本地源文件存在。

 

ADD  与 COPY 类似,复制文件到镜像。不同的是,如果文件是归档文件(tar, zip, tgz, xz 等),会被自动解压。

 

ENV  设置环境变量,格式: ENV key=value或ENV key value,运行容器后,可直接在容器中使用。

 

EXPOSE  暴露容器中指定的端口,只是一个声明,主要用户了解应用监听的端口。

 

VOLUME  挂载卷到容器,需要注意的是,保存镜像时不会保存卷中的数据。

 

WORKDIR  设置当前工作目录,后续各层的当前目录都被指定。

 

RUN  在容器中运行指定的命令。

 

CMD  容器启动时运行的命令。Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效。CMD 可以被 docker run 之后的参数替换。

 

ENTRYPOINT  设置容器启动时运行的命令。Dockerfile 中可以有多个 ENTRYPOINT 指令,但只有最后一个生效。CMD 或 docker run 之后的参数会被当做参数传递给 ENTRYPOINT,这个是与CMD的区别。

 

精简镜像的好处不言而喻,即可以节省存储存储空间也可,也可减少镜像传输时间,减少带宽的消耗,加快传输。

 

在开始制作镜像之前,我们先了解一下容器镜像的基本基本理论知识。

 

容器镜像中最重要的概念就是layers,即镜像层。

 

640?wx_fmt=png

 

镜像层依赖于一系列的底层技术,比如文件系统(filesystems)、写时复制(copy-on-write)、联合挂载(union mounts)等技术,这些技术的细节在此我们不再赘述,感兴趣的同学可以直接直接查看Docker 官方文档(https://docs.docker.com/storage/storagedriver/)进行学习。

 

总的来说,精简镜像我们最需要记住的一句话即:

 

“在Dockerfile中,每条指令都会创建一个镜像层,继而会增加镜像整体的大小。”

 

下面我们以一个示例来说明一下:

 

我们pull 一个镜像,以busybox为例:

 

[root@work ~]# docker pull hub.c.163.com/library/busybox:latest

Trying to pull repository hub.c.163.com/library/busybox ...

latest: Pulling from hub.c.163.com/library/busybox

aab39f0bc16d: Pull compl`ete

Digest: sha256:662af1d642674367b721645652de96f9c147417c2efb708eee4e9b7212697762

Status: Downloaded newer image for hub.c.163.com/library/busybox:latest

 

# 看下镜像大小

[root@work ~]# docker images | grep busybox

hub.c.163.com/library/busybox                          latest                                           d20ae45477cb        18 months ago       1.129 MB

 

从上面结果看我们pull下来的镜像大小只有1.129MB。

 

下面我们编写一个Dockerfile文件,文件中我们新建一个目录,目录中新建一个100MB的文件,最后我们删掉新建的文件。

 

Dockerfile 内容如下:

 

#基镜像

FROM hub.c.163.com/library/busybox:latest

#新建目录

RUN mkdir /tmp/dir1

#新建一个100MB的文件

RUN dd if=/dev/zero of=/tmp/dir1/file1 bs=1M count=100

#删除文件

RUN rm /tmp/dir1/file1

 

从Dockerfile 内容看,其实我们基本什么都没干。

 

然后我们用这个Dockerfile构建新建的镜像,并查看新镜像的大小:

 

[root@work ~]# docker build -t busybox:v1 .

Sending build context to Docker daemon 1.307 GB

Step 1 : FROM hub.c.163.com/library/busybox:latest

 ---> d20ae45477cb

Step 2 : RUN mkdir /tmp/dir1

 ---> Running in 63fa5f27c779

 ---> da95ea8ae5ee

Removing intermediate container 63fa5f27c779

Step 3 : RUN dd if=/dev/zero of=/tmp/dir1/file1 bs=1M count=100

 ---> Running in d3e8bbb4f151

100+0 records in

100+0 records out

104857600 bytes (100.0MB) copied, 0.247500 seconds, 404.0MB/s

 ---> 42b721238144

Removing intermediate container d3e8bbb4f151

Step 4 : RUN rm -rf /tmp/dir1/file1

 ---> Running in 6b51b633fb21

 ---> 04096cc5d718

Removing intermediate container 6b51b633fb21

Successfully built 04096cc5d718

 

# 查看镜像信息

[root@work ~]# docker images | grep busybox

busybox                                                v1                                               04096cc5d718        58 seconds ago      106 MB

hub.c.163.com/library/busybox                          latest                                           d20ae45477cb        18 months ago       1.129 MB

 

从上面的结果可以看出,虽然在Dockerfile中我们将新建的100MB的文件删除了,但新镜像的大小仍大于100MB。

 

多出了100多MB,这是为何?其实这点和git类似,Docker镜像和git都用到了写时复制技术,git每次提交时都会保存一个文件的版本,Dockerfile每行指令都会增加整体镜像的大小,即使我们什么都没做。

 

下面我们开始说下本文的重点:镜像精简。

 

我们以最常见的nosql数据库Redis为例,一步步来介绍如何制作更精简的Docker 镜像。

 

首先我们编写下构建Redis镜像的Dockerfile文件,具体内容如下:

FROM hub.c.163.com/library/ubuntu:trusty

#redis 版本

ENV VER     3.0.0

ENV TARBALL http://download.redis.io/releases/redis-$VER.tar.gz

RUN apt-get update

#安装依赖的工具

RUN apt-get install -y  curl make gcc

#下载redis源码包并解压

RUN curl -L $TARBALL | tar zxv

#进入解压后的目录

WORKDIR  redis-$VER

#编译redis源码

RUN make

#安装redis

RUN make install

WORKDIR /

#清理前面安装的依赖工具

RUN apt-get remove -y --auto-remove curl make gcc

RUN apt-get clean

RUN rm -rf /var/lib/apt/lists/*  /redis-$VER

#运行redis

CMD ["redis-server"]

 

利用上面的Dockerfile 构建镜像:

[root@work ~]# docker build -t redis:3.0.0 .

……

……

Removing intermediate container b55656487022

Successfully built 7df9c7899ae3

    

查看构建出的镜像大小:

[root@work ~]# docker images | grep redis

redis                                                  3.0.0                                            7df9c7899ae3        10 hours ago        359.7 MB

从结果看构建出优化前的镜像约为360MB。

 

下面我们逐步优化。 

 

640?wx_fmt=png

1. 选用更小的基镜像

 

常用的linux系统一般有CentOS、Debian、Ubuntu,三者中Debian更轻量,且Debian系统镜像中提供的功能一般也是够用的,三个系统镜像尺寸对比如下:

        

镜像名称            标签         镜像 ID        镜像大小

--------              ------        ------------          --------

centos              7          214a4932132a     215.7 MB

centos              6          f6808a3e4d9e     202.6 MB

ubuntu              trusty     d0955f21bf24     188.3 MB

ubuntu              precise    9c5e4be642b7     131.9 MB

debian              jessie     65688f7c61c4     122.8 MB

debian              wheezy     1265e16d0c28     84.96 MB

 

在此我们以上面最小的镜像debian:wheezy 作为即镜像,重新进行构建:

 

Dockerfile内容:

FROM hub.c.163.com/library/debian:wheezy

#redis 版本

ENV VER     3.0.0

ENV TARBALL http://download.redis.io/releases/redis-$VER.tar.gz

RUN apt-get update

#安装依赖的工具

RUN apt-get install -y  curl make gcc

#下载redis源码包并解压

RUN curl -L $TARBALL | tar zxv

#进入解压后的目录

WORKDIR  redis-$VER

#编译redis源码

RUN make

#安装redis

RUN make install

WORKDIR /

#清理前面安装的依赖工具

RUN apt-get remove -y --auto-remove curl make gcc

RUN apt-get clean

RUN rm -rf /var/lib/apt/lists/*  /redis-$VER

#运行redis

CMD ["redis-server"]

 

构建新镜像:

[root@work ~]# docker build -t redis:3.0.0-v2 .

……

……

Removing intermediate container 3498689792ce

Successfully built 4faa1aa0936d

 

对比两个镜像大小:

[root@work ~]# docker images | grep redis

redis                                                  3.0.0-v2                                         4faa1aa0936d        38 seconds ago      228.5 MB

redis                                                  3.0.0                                            7df9c7899ae3        11 hours ago

 

从结果看更换基镜像后的新镜像减少了37%,精简效果还算可以,但精简效果并未达到我们的目标。

 

如果仔细看的话我们会发现只有85MB的debian基镜像,在构建后增加到了228MB可见此处还有很大的优化空间,后续的优化就需要用到我们在上文中说到的镜像层相关的知识了。

      

640?wx_fmt=png

 

2. 合并Dockerfile中指令

 

Dockerfile 中指令的合并一般是指RUN指令的合并。

 

我们可以通过&&符号和/ 将Dockerfile 中的多个RUN指令合并成一条RUN 指令,此种方式一般精简效果较好。

 

优化后的Dockerfile 内容如下:

FROM hub.c.163.com/library/debian:wheezy

#redis 版本

ENV VER     3.0.0

ENV TARBALL http://download.redis.io/releases/redis-$VER.tar.gz

RUN apt-get update && \

apt-get install -y  curl make gcc &&\

curl -L $TARBALL | tar zxv  && \

cd  redis-$VER  && \

make  && \

make install && \

cd /  && \

apt-get remove -y --auto-remove curl make gcc && \

apt-get clean  && \

rm -rf /var/lib/apt/lists/*  /redis-$VER

#运行redis

CMD ["redis-server"]

 

构建新镜像:

 

[root@work ~]# docker build -t redis:3.0.0-v3 .

……

……

Removing intermediate container 9e5cffcd8bdb

Successfully built dafd91993dfb

 

查看镜像大小:

[root@work ~]# docker images | grep redis

redis                                                  3.0.0-v3                                         dafd91993dfb        33 seconds ago      102.3 MB

redis                                                  3.0.0-v2                                         4faa1aa0936d        15 minutes ago      228.5 MB

redis                                                  3.0.0                                            7df9c7899ae3        11 hours ago        359.7 MB

 

从结果看镜像大小约缩减72%,可见合并Dockerfile指令的方式精简效果较明显,新镜像只比基镜像增加约10MB。

合并Dockerfilec指令精简镜像这种方式是最常用的精简镜像尺寸的方式。

 

3. 使用最精简的基镜像

 

上文中第1步中,我们使用的基镜像为Debian镜像,约89MB,但如果我们只是安装Redis 服务的话不一定非得使用这么大的系统镜像,我们可以借助一些更小的镜像,如scratch、busybox、alpine等,这些镜像大小往往小于5MB,因此我们可以直接以此作为基镜像来构建新的Redis镜像。

 

此处我们以scratch作为基镜像构建Redis。scratch镜像往往只有1~5MB大小。

 

Scrach 是一个空镜像,只能用于构建镜像。在构建一些基础镜像,如debian、busybox时非常有用。Scrach也常用于构建超小的镜像,如构建一个只包含所有库的二进制文件。

 

但使用最精简的基镜像我们还需要做些额外的工作,具体过程见下文。

 

4. 提取.so库

 

了解过Redis源码的话大家会知道Redis 开发语言为C语言,会依赖一些.so库,因此我们需要先准备好编译Redis 所需的.so文件。

 

我们通过前面构建好的redis:3.0.0-v3镜像运行容器,然后进入容器中获取下redis依赖的.so文件。

 

# 后台运行容器:

[root@work ~]# docker run --name redisv3 -d  redis:3.0.0-v3

ab361e7fc2e70b5b45fa1545917ee92158bb859e833c3f7fcfb80e43bb69cb0c

 

# 查看容器运行状态

[root@work ~]# docker ps

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

ab361e7fc2e7        redis:3.0.0-v3      "redis-server"      3 seconds ago       Up 3 seconds                            redisv3

 

# 进入容器

[root@work ~]# docker exec -ti redisv3 /bin/bash

root@ab361e7fc2e7:/#

 

# 查看redis-server 二进制文件位置

root@ab361e7fc2e7:/# which redis-server

/usr/local/bin/redis-server

 

# 查看redis-server依赖的.so文件

root@ab361e7fc2e7:/# ldd /usr/local/bin/redis-server

linux-vdso.so.1 =>  (0x00007ffedfd01000)

libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f0de7a5e000)

libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f0de785a000)

libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f0de763d000)

libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0de72b0000)

/lib64/ld-linux-x86-64.so.2 (0x00007f0de7ce4000)

 

将编译Redis 需要的所有依赖打包:

root@ab361e7fc2e7:/# mkdir so

root@ab361e7fc2e7:/# cp usr/local/bin/redis-server so/

root@ab361e7fc2e7:/# cp lib/x86_64-linux-gnu/libm.so.6 so/

root@ab361e7fc2e7:/# cp lib/x86_64-linux-gnu/libpthread.so.0 so/

root@ab361e7fc2e7:/# cp lib/x86_64-linux-gnu/libc.so.6 so/      

root@ab361e7fc2e7:/# cp lib64/ld-linux-x86-64.so.2 so/  

root@ab361e7fc2e7:/# cd so

root@ab361e7fc2e7:/# tar zcvf so.tar.gz ./*

so/

so/redis-server

so/libm.so.6

so/libpthread.so.0

so/libc.so.6

so/ld-linux-x86-64.so.2

 

# 将打包好的文件从容器拷贝出来

[root@work ~]# docker cp redisv3:/so/so.tar.gz .

 

编写Dockerfile 文件,具体内容如下:

 

FROM scratch

# 添加依赖的库文件

ADD  so.tar.gz  /

# redis 配置文件,需要自己准备一份

COPY redis.conf     /etc/redis/redis.conf

# 暴露的端口

EXPOSE 6379

CMD ["redis-server"]

 

构建新镜像:

 

[root@work ~]# docker build -t redis:3.0.0-v4 .

Sending build context to Docker daemon 1.316 GB

Step 1 : FROM scratch

 --->

Step 2 : ADD so.tar.gz /

 ---> Using cache

 ---> 82b2b6def214

Step 3 : COPY redis.conf /etc/redis/redis.conf

 ---> 3f382da261be

Removing intermediate container 60af6a5ab042

Step 4 : EXPOSE 6379

 ---> Running in 78c541686668

 ---> 043ed6cf87e0

Removing intermediate container 78c541686668

Step 5 : CMD redis-server

 ---> Running in 2c8b9fb0547d

 ---> 75d828ebf3aa

Removing intermediate container 2c8b9fb0547d

Successfully built 75d828ebf3aa

 

对比镜像大小:

[root@work ~]# docker images | grep redis

redis                                                  3.0.0-v4                                         75d828ebf3aa        30 seconds ago      6.938 MB

redis                                                  3.0.0-v3                                         dafd91993dfb        About an hour ago   102.3 MB

redis                                                  3.0.0-v2                                         4faa1aa0936d        About an hour ago   228.5 MB

redis                                                  3.0.0                                            7df9c7899ae3        12 hours ago        359.7 MB

 

从结果看精简效果显著提高,基于scratch构建的新镜像大小只有6.9MB,相比之前的359MB、228MB、102MB,新镜像空间占用已经很少。

 

以上即是本文精简Docker 镜像的整个过程。

 

除了上面我们介绍的精简方法之外还有一些常见的精简方式,如使用镜像压缩工具docker-squash,但此种方式压缩效果并不明显,因此在此我们并未介绍,感兴趣的同学可以自己尝试下。


640?wx_fmt=png


福利

扫描添加小编微信,备注“姓名+公司职位”,加入【云计算学习交流群】,和志同道合的朋友们共同打卡学习!


640?wx_fmt=jpeg


推荐阅读:

  • 趣挨踢 | 用大数据扒一扒蔡徐坤的真假流量粉

  • 姚期智提出的"百万富翁"难题被破解? 多方安全计算MPC到底是个什么鬼?

  • 全民 AI !教育部宣布 35 所高校新增 AI 本科专业

  • 深度 | 人工智能究竟能否实现?

  • 程序媛报告:调查了 12,000 名女性开发者发现,女性比男性更懂 Java!

  • 程序员怒了!你敢削减专利奖金,我敢拒绝提交代码!


640?wx_fmt=png喜欢就点击“在看”吧


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/524300.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux环境 Jenkins集成构建SonarQube

参考链接: https://www.linuxhub.org/?p4450 https://blog.51cto.com/damaicha/2118766 https://max.book118.com/html/2019/0107/8067110025002000.shtmJenkins集成构建SonarQube 一、前提准备 应用服务器apache-tomcat-9.0.20.tar.gzjenkinsjenkins.warmysql版…

Windows OpenGL ES 图像对比度调节

目录 一.OpenGL ES图像对比度调节 1.原始图片2.效果演示 二.OpenGL ES 图像对比度调节源码下载三.猜你喜欢 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 基础 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 特效 零基…

dubbo之.xml配置文件报错

【dubbo之.xml配置文件报错】Multiple annotations found at this line: - cvc-complex-type.2.4.c: The 异常现象: 1.dubbo配置xml文件报错 “Multiple annotations found at this line:- cvc-complex-type.2.4.c: The matching wildcard is strict, but no decl…

验证电脑的字节序

字节序的分类 大端字节序:低地址存高位,高地址存底位,网络字节序又叫大端端字节序; 小端字节序:低地址存地位,高地址存高位; 验证示例 记录2个示例来说明电脑采用的大端字节序还是小端字节序…

刚刚出新的Kubernetes 却曝出了“高危”安全漏洞;亚马逊将推免费新闻视频服务,对标苹果 | 极客头条...

关注并标星星CSDN云计算每周三次,打卡即read更快、更全了解泛云圈精彩newsgo go go 被宣布取消的AirPower(图片来源网络)【4月1日 星期一】云の声音5G最极致的应用领域应该是8K电视。——富士康科技集团副总裁、富士康企业大学创校校长 陈振国…

Maven实战手册

一、maven的作用: 1.1jar包的统一依赖管理,节省空间1.2项目的一键构建 二、Maven能做什么? 2.1编译 测试(junit) 运行 打包 部署 三、Maven的安装配置署 3.1下载安装3.2 官网链接:http://maven.apache.org/download.cgi 3.3下…

Windows OpenGL 图像对比度调节

零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 基础 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 特效 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 转场 零基础 OpenGL ES 学习路线推荐…

大端字节序与小端字节序的转换

逐步加深对字节操作的理解,记录一下大端字节序与小端字节序的转换,开发环境是vs2010,项目类型是控制台输出程序,下面是代码实现: // ByteOrder_demo.cpp : 定义控制台应用程序的入口点。 //#include "stdafx.h&q…

2019年技术盘点微服务篇(一) | 程序员硬核评测

戳蓝字“CSDN云计算”关注我们哦!作者:孙浩峰过去几年来,“微服务架构”方兴未艾,尽管这种架构风格没有确切的定义,但我们已经看到许多项目凭借此架构取得了积极的结构,因此对于许多开发者来说,…

虚拟化精华问答 | 虚拟化的关键技术有哪些?

在实际的生产环境中,虚拟化技术主要用来解决高性能的物理硬件产能过剩和老的旧的硬件产能过低的重组重用,透明化底层物理硬件,从而最大化的利用物理硬件。今天就让我们一起来看看关于虚拟化的精华问答吧。1Q:什么是虚拟化&#xf…

idea工具拉取GitHub远程代码

需求:idea工具拉取GitHub远程代码: 项目代码地址: gitgithub.com:gb-heima/createNewProject.git可以先进行test测试连接情况:

Windows OpenGL ES 图像饱和度调节

目录 一.OpenGL ES 图像饱和度调节 1.原始图片2.效果演示 二.OpenGL ES 图像饱和度调节源码下载三.猜你喜欢 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 基础 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 特效 零基…

云重磅 | 阿里巴巴平均每天纳税超1.4亿;谷歌、Face book与AWS将共建美欧新海底电缆;阿里发布谣言粉碎机:1秒辨真伪...

戳蓝字“CSDN云计算”关注我们哦!嗨,大家好,重磅君带来的【云重磅】特别栏目,如期而至,每周二第一时间为大家带来重磅新闻。把握技术风向标,了解行业应用与实践,就交给我重磅君吧!重…

远程GitHub中的项目拉取到本地

需求:远程GitHub中的项目拉取到本地: 有2种方式: 第1种:直接下载到本地: 第2种(建议使用): 通过git工具,进行远程拉取代码,前提已安装了git 项目代码地址: gitgithu…

Windows OpenGL 图像饱和度调节

目录 一.OpenGL 图像饱和度调节 1.原始图片2.效果演示 二.OpenGL 图像饱和度调节源码下载三.猜你喜欢 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 基础 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 特效 零基础 Op…

轻松了解面试官心理!ElasticSearch写入数据的工作原理是什么? | 技术头条

戳蓝字“CSDN云计算”关注我们哦!作者:手留余香转自: Java架构沉思录面试题es 写入数据的工作原理是什么啊?es 查询数据的工作原理是什么啊?底层的 lucene 介绍一下呗?倒排索引了解吗?面试官心理…

OpenGL 伽马线

目录 一.OpenGL 伽马线 1.IOS Object-C 版本2.Windows OpenGL ES 版本3.Windows OpenGL 版本 二.OpenGL 伽马线 GLSL Shader三.猜你喜欢 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 基础 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 &…

GitHub 创建项目

需求:在github上创建一个新的远程仓库:

Qt界面设计器中的界面预览与程序运行时界面不一样

用QtCreate创建的界面应用程序,在ui文件中拖入所需的控件,并进行布局,布局完成后在ui文件的工具->form Editor->预览可查看当前界面。问题便是:通过预览查看界面布局没有问题,但运行程序时界面与预览的界面存在偏差。 我做…

找了前锤子CTO钱晨加入SLG ,百度终于认了……新的一年第4批CDN牌照公布;亚马逊开设云计算技术培训工作 | 极客头条...

关注并标星星CSDN云计算极客头条:速递、最新、绝对有料。这里有企业新动、这里有业界要闻,打起十二分精神,紧跟fashion你可以的!每周三次,打卡即read更快、更全了解泛云圈精彩newsgo go go 百度宣布正式进军社交领域并…