如何瘦身 Git 仓库管理制度

用Bitnami安装包安装了redmine,如何发布将git代码仓库发布出去呢? - 知乎知乎 - 有问题上知乎2被浏览487分享邀请回答赞同 添加评论分享收藏感谢收起写回答你的浏览器禁用了JavaScript, 请开启后刷新浏览器获得更好的体验!
在构建Docker容器时,我们应尽可能减小镜像的大小。使用共享层的镜像尺寸越小,其传输和部署速度越快。
不过在每个RUN语句都会创建一个新层的情况下,如果我们需要获取镜像完成前的中间产物,又如何控制其大小呢?
你可能已经注意到市面上多数的Dockerfile都会使用类似这样的招数:
FROM&ubuntu
RUN&apt-get&update&&&&apt-get&install&vim
为什么要使用&&,而不像这样运行两个RUN语句?
FROM&ubuntu
RUN&apt-get&update
RUN&apt-get&install&vim
从Docker 1.10起,COPY、ADD和RUN语句会在镜像中添加新层。上述示例将创建两个层,而不是一个。
层跟Git提交类似。
Docker层存储了镜像上一版本和当前版本之间的差异。与Git提交类似,层有利于与其他仓库或镜像进行共享。
实际上,当我们从Registry请求镜像时,我们只会下载那些不存在的层。这种方式让镜像共享更高效。
但是,层是有代价的。
层会占用空间,层越多,最终的镜像就越大。Git仓库在这方面是相似的。因为Git需要保存提交之间的所有变更,仓库的大小会随着层数的增加而增加。
在过去,做法就是像第一个例子那样将几个RUN语句合并在一行中。
然后就没有然后了。
使用Docker多阶段构建将层合并为一当Git仓库变得越来越大时,我们可以放弃所有过往信息,将历史提交合并成一个。
使用Docker的多阶段构建,我们也能实现类似的功能。
接下来的例子中,我们将构建一个Node.js容器。
首先是index.js文件:
const&express&=&require('express')
const&app&=&express()
app.get('/',&(req,&res)&=&&res.send('Hello&World!'))
app.listen(3000,&()&=&&{
console.log(`Example&app&listening&on&port&3000!`)
接着是package.json文件:
&name&:&&hello-world&,
&version&:&&1.0.0&,
&main&:&&index.js&,
&dependencies&:&{
&express&:&&^4.16.2&
&scripts&:&{
&start&:&&node&index.js&
通过以下Dockerfile来打包该应用:
FROM&node:8
EXPOSE&3000
WORKDIR&/app
COPY&package.json&index.js&./
RUN&npm&install
CMD&[&npm&,&&start&]
构建镜像:
$&docker&build&-t&node-vanilla&.
然后我们可以这么验证它:
$&docker&run&-p&&-ti&--rm&--init&node-vanilla
访问应该就能看到“Hello World!”欢迎语。
这个Dockerfile文件中有一个COPY和一个RUN语句。在我们的预期中,在基础镜像上应该至少有两层:
$&docker&history&node-vanilla
IMAGE&&&&&&&&&&CREATED&BY&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&SIZE
075d229d3f48&&&/bin/sh&-c&#(nop)&&CMD&[&npm&&&start&]&&&&&&&&&&0B
bc8c3cc813ae&&&/bin/sh&-c&npm&install&&&&&&&&&&&&&&&&&&&&&&&&&&2.91MB
bac31afb6f42&&&/bin/sh&-c&#(nop)&COPY&multi:3071ddd…&&&364B
500a9fbef90e&&&/bin/sh&-c&#(nop)&WORKDIR&/app&&&&&&&&&&&&&&&&&&0B
78b28027dfbf&&&/bin/sh&-c&#(nop)&&EXPOSE&3000&&&&&&&&&&&&&&&&&&0B
b87c2ad8344d&&&/bin/sh&-c&#(nop)&&CMD&[&node&]&&&&&&&&&&&&&&&&&0B
&missing&&&&&&&/bin/sh&-c&set&-ex&&&&&&for&key&in&&&&&6A010…&&&4.17MB
&missing&&&&&&&/bin/sh&-c&#(nop)&&ENV&YARN_VERSION=1.3.2&&&&&&&0B
&missing&&&&&&&/bin/sh&-c&ARCH=&&&&dpkgArch=&$(dpkg&--print…&&&56.9MB
&missing&&&&&&&/bin/sh&-c&#(nop)&&ENV&NODE_VERSION=8.9.4&&&&&&&0B
&missing&&&&&&&/bin/sh&-c&set&-ex&&&&&&for&key&in&&&&&94AE3…&&&129kB
&missing&&&&&&&/bin/sh&-c&groupadd&--gid&1000&node&&&&&&use…&&&335kB
&missing&&&&&&&/bin/sh&-c&set&-&&apt-get&&&apt-ge…&&&324MB
&missing&&&&&&&/bin/sh&-c&apt-get&update&&&&apt-get&install…&&&123MB
&missing&&&&&&&/bin/sh&-c&set&-&&if&!&command&-v&gpg&&&/…&&&0B
&missing&&&&&&&/bin/sh&-c&apt-get&update&&&&apt-get&install…&&&44.6MB
&missing&&&&&&&/bin/sh&-c&#(nop)&&CMD&[&bash&]&&&&&&&&&&&&&&&&&0B
&missing&&&&&&&/bin/sh&-c&#(nop)&ADD&file:1dd78abd…&&&123MB
实际上,最终的镜像上添加了五个新层:Dockerfile里的每条语句一层。
我们来试试Docker的多阶段构建。
我们使用的Dockerfile与之前相同,不过分成了两部分:
FROM&node:8&as&build
WORKDIR&/app
COPY&package.json&index.js&./
RUN&npm&install
FROM&node:8
COPY&--from=build&/app&/
EXPOSE&3000
CMD&[&index.js&]
Dockerfile的第一部分创建了三个层。接着这些层被合并复制到第二个阶段中。然后又在这个镜像之上添加了两层,最终变成三个层。
现在来验证一下。首先,构建该容器:
$&docker&build&-t&node-multi-stage&.
查看其构建历史:
$&docker&history&node-multi-stage
IMAGE&&&&&&&&&&CREATED&BY&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&SIZE
331b81a245b1&&&/bin/sh&-c&#(nop)&&CMD&[&index.js&]&&&&&&&&&&&&&0B
bdfc932314af&&&/bin/sh&-c&#(nop)&&EXPOSE&3000&&&&&&&&&&&&&&&&&&0B
fa6&&&/bin/sh&-c&#(nop)&COPY&dir:e2b57dff89be62f77…&&&1.62MB
b87c2ad8344d&&&/bin/sh&-c&#(nop)&&CMD&[&node&]&&&&&&&&&&&&&&&&&0B
&missing&&&&&&&/bin/sh&-c&set&-ex&&&&&&for&key&in&&&&&6A010…&&&4.17MB
&missing&&&&&&&/bin/sh&-c&#(nop)&&ENV&YARN_VERSION=1.3.2&&&&&&&0B
&missing&&&&&&&/bin/sh&-c&ARCH=&&&&dpkgArch=&$(dpkg&--print…&&&56.9MB
&missing&&&&&&&/bin/sh&-c&#(nop)&&ENV&NODE_VERSION=8.9.4&&&&&&&0B
&missing&&&&&&&/bin/sh&-c&set&-ex&&&&&&for&key&in&&&&&94AE3…&&&129kB
&missing&&&&&&&/bin/sh&-c&groupadd&--gid&1000&node&&&&&&use…&&&335kB
&missing&&&&&&&/bin/sh&-c&set&-&&apt-get&&&apt-ge…&&&324MB
&missing&&&&&&&/bin/sh&-c&apt-get&update&&&&apt-get&install…&&&123MB
&missing&&&&&&&/bin/sh&-c&set&-&&if&!&command&-v&gpg&&&/…&&&0B
&missing&&&&&&&/bin/sh&-c&apt-get&update&&&&apt-get&install…&&&44.6MB
&missing&&&&&&&/bin/sh&-c&#(nop)&&CMD&[&bash&]&&&&&&&&&&&&&&&&&0B
&missing&&&&&&&/bin/sh&-c&#(nop)&ADD&file:1dd78abd…&&&123MB
符合预期!文件大小有变化么?
$&docker&images&|&grep&node-
node-multi-stage&&&331b81a245b1&&&678MB
node-vanilla&&&&&&&075d229d3f48&&&679MB
是的,新的镜像要小一点点。
看起来还不错!尽管应用本身已经做了精简,我们还是减少了其整体大小。
不过,镜像依然很大!
要让它变得更小一点,我们还能做点什么?
使用Distroless移除容器中的所有累赘目前的镜像不仅含有Node.js,还含有yarn、npm、bash以及大量其他二进制文件。同时,它是基于Ubuntu的。因此拥有一个完整的操作系统以及所有的二进制文件和实用程序。
这些在运行容器时都不是必需的。我们唯一的依赖项是Node.js。
Docker容器应封装在单一进程中,且只包含运行所需的最精简内容。我们不需要一个操作系统。
实际上,除了Node.js,其他都可以移除。
那么要怎么做呢?
幸运的是,Google也有同样的想法,他们带来了。
有如其仓库说明所述:
“Distroless”镜像只包含应用程序及其运行时依赖。不包含包管理器、Shell以及其他标准Linux发行版中能找到的其他程序。这正是我们所需要的!
我们可以调整Dockerfile文件来使用这个新的基础镜像:
FROM&node:8&as&build
WORKDIR&/app
COPY&package.json&index.js&./
RUN&npm&install
FROM&gcr.io/distroless/nodejs
COPY&--from=build&/app&/
EXPOSE&3000
CMD&[&index.js&]
然后像平常那样编译镜像:
$&docker&build&-t&node-distroless&.
应用程序应能正常运行。要验证这一点,可以像这样运行容器:
$&docker&run&-p&&-ti&--rm&--init&node-distroless
访问页面即可。
这个未包含额外程序的镜像会多小呢?
$&docker&images&|&grep&node-distroless
node-distroless&&&7b4db3b7f1e5&&&76.7MB
仅仅76.76MB!
比前一个镜像少了600MB!
真是个好消息!不过在使用Distroless时有些事项需要注意。
容器运行时,如果想对其进行检查,可以这么做:
$&docker&exec&-ti&&替换成_docker_id&&bash
上述命令将附加到容器中并运行bash,这与发起一个SSH会话相近。
不过由于Distroless是原始操作系统的精简版本,不包含额外的程序。容器里并没有Shell!
如果没有Shell,要如何附加到运行的容器中呢?
好消息和坏消息是,做不到。
坏消息是我们只能运行容器中的二进制程序。这里能运行的只有Node.js:
$&docker&exec&-ti&&替换成_docker_id&&node
好消息是因为没有Shell,如果黑客入侵了我们的应用程序并获取了容器的访问权限,他也无法造成太大的损害。也就是说,程序越少则尺寸越小也越安全。不过,代价是调试更麻烦。
需要注意的是,我们不应该在生产环境中附加到容器中进行调试,而应依靠正确的日志和监控。如果我们既希望能调试,又关心尺寸大小,又该怎么办?
使用Alpine作为更小的基础镜像我们可以使用Alpine取代Distroless来作为基础镜像。
一个基于和、面向安全的轻量级Linux发行版。换言之,它是一个尺寸更小、更安全的Linux发行版。
是否言过其实,我们来检查一下这个镜像是否更小。
修改之前的Dockerfile并使用node:8-alpine:
FROM&node:8&as&build
WORKDIR&/app
COPY&package.json&index.js&./
RUN&npm&install
FROM&node:8-alpine
COPY&--from=build&/app&/
EXPOSE&3000
CMD&[&npm&,&&start&]
构建该镜像:
$&docker&build&-t&node-alpine&.
现在看一下它的大小:
$&docker&images&|&grep&node-alpine
node-alpine&&&aa1f85f8e724&&&69.7MB
甚至比Distroless镜像还要小!
我们来看看能不能附加运行中的容器。
首先,启动容器:
$&docker&run&-p&&-ti&--rm&--init&node-alpine
Example&app&listening&on&port&3000!
现在附加到容器中:
$&docker&exec&-ti&9d8e97e307d7&bash
OCI&runtime&exec&failed:&exec&failed:&container_linux.go:296:&starting&container&process&caused&&exec:&\&bash\&:&executable&file&not&found&in&$PATH&:&unknown
运气不佳。但或许容器有sh这个Shell?
$&docker&exec&-ti&9d8e97e307d7&sh
很好!我们既可以附加到运行的容器中,得到的镜像尺寸也很小。
听起来很棒,不过有一个小问题。
Alpine基础镜像是基于muslc的,这是一个C的替代标准库。
但是,多数Linux发行版,比如Ubuntu、Debian及CentOS都是基于glibc的。这两个库照理应该实现了相同的接口。
不过,它们的目标不同:
glibc最常用,速度更快muslc占用空间更少,以安全为核心
在编译应用程序时,多数情况下是使用某个libc来编译的。如果想在其他libc中使用,只能重新编译。
也就是说,使用Alpine镜像来构建容器可能会造成不可预期的问题,因为使用的是不同的C标准库。
特别是在处理预编译的二进制文件时,比如Node.js的C++扩展,这个差异更明显。
举个例子,PhantomJS预置包就无法在Alpine中工作。
怎么选择基础镜像?Alpine、Distroless或是原生镜像到底用哪个?
如果是在生产环境中运行,并且注重安全性, Distroless镜像可能会更合适。
Docker镜像中每增加一个二进制程序,就会给整个应用程序带来一定的风险。
在容器中只安装一个二进制程序即可降低整体风险。
举个例子,如果黑客在运行于Distroless的应用中发现了一个漏洞,他也无法在容器中创建Shell,因为根本就没有。
注意:。如果更在意要是大小,则可以换成Alpine基础镜像。
这两个都很小,代价是兼容性。Alpine用了一个稍稍有点不一样的C标准库——muslc。时不时会碰到点兼容性的问题。比如和。
原生基础镜像非常适合用于测试和开发。
它的尺寸比较大,不过用起来就像你主机上安装的Ubuntu一样。并且,你能访问该操作系统里有的所有二进制程序。
下面,回顾一下各个镜像大小:
node:8 681MB
node:8结合多阶段构建 678MB
gcr.io/distroless/nodejs 76.7MB
node:8-alpine 69.7MB
原文链接:(翻译:)
要回复文章请先或
齐家网技术总监随笔 - 简书
你要草莓吗
一个非要把Charlie翻译成茶黎的执着骚年
图/来自网络 少小时,知西藏,高原之地,世界屋脊,祖国西南边陲,和印度、尼泊尔接壤。 心里一直向往之,知道西藏人好佛,全族信仰。 但未有机缘直至50岁时,夏中,6月13日随旅行社前往。 出...
号,我没有选择睡懒觉,而是选择起床去参加万万思想行的分享会。这次来的伙伴不多,但是现场彼此之间的交流及碰撞让我这边感触还是挺深刻的。 这次的演讲主题是2016年的总结及成长,2017年的展望! 我给自己的2016打了60分,勉强及格!从年初给自己定的目标来看...
生好读书,所读之书类也杂,其中尤喜读随笔。近来闲暇,读全球著名华人经济学家随笔之一的《人类理性的盲点》。作者是一个留学国外、从事工商管理和经济学研究的人士。从五个方面“带着爱情去解剖中国与人生,带着常情去学习西方与世界”以理性的眼、感性的心去看中国,看人生。写游学生涯,解读...
总觉得成长是一件残酷的事。 生命中那些来去的人,那一张张变换的脸,那匆匆而过的景,都诉说着时间的流逝,岁月的无情。那回不去的青春,再也回不去的岁月,将是我一生中最美,最珍贵的时光。 渐渐长大,就会渐渐明白,再也不会有那么没心没肺的笑,再也不会有一个人不计回报的对你好,再也不...
明日赴考场随笔
考博随笔 往昔苦读月半沉, 今朝志满赴龙门。 不为功名利禄故, 只争光阴报国恩。
时间能抹去大部分记忆,但总有一些日子无论经过多少的时间,记忆依然如此清晰。 曾经炎热的一天,和很多同学去海边,第一次在海水里游泳。但只记得那时候无忧无虑,特别的开心。也会时常的怀念这件事,也许是现在感觉过得没有那时候好吧! 小时候一个人拿粮食去换水果吃。之后才知道被老板糊弄...
一年到头我们总问自己成长了没有,很多人觉得肯定成长了。但更多人的情况是:长是长了,成不成,不知道。 “长”,只是现在比过去好一点的感觉。以“长”为目标,不能成为牛人,只能成为一个不焦虑的人。就像那些一年阅读100本书的人,就是典型的以“长”为目标,这是很吓人的。你会发现书是...
其实,你也可以成为大神 “程序盲”、“程序通”,看似两个不搭边界的词汇却出现在了同一个人的身上,她就是河北港口集团港口机械有限公司的首席车工、省劳模申晓雪。“她电脑编程 水平曾受到很多专家、教授的赞誉。”这是从她的一名同事口中得知的,那么她是如何从一名”程序盲”蜕变成了一名...
create user aa@localhost identified by 'password'; 删除用户的时候: drop user aa@(不要加引号) 如果用 delete...
http://blog.mallol.cn/如何给git仓库瘦身删除大文件.html共6页:&1&&&&&&&
23:48 阅读:62 评论:0&&
19:54 阅读:89 评论:0&&
21:22 阅读:121 评论:0&&
10:31 阅读:3241 评论:7&&
11:12 阅读:272 评论:1&&
10:46 阅读:14353 评论:0&&
12:03 阅读:4447 评论:3&&
13:35 阅读:645 评论:1&&
22:36 阅读:199 评论:0&&
20:39 阅读:625 评论:0&&
共6页:&1&&&&&&&原文:http://www.cnblogs.com/wilber2013/p/4189920.html
Git基本概念
在Git中,我们将需要进行版本控制的文件目录叫做一个仓库(repository),每个仓库可以简单理解成一个目录,这个目录里面的所有文件都通过Git来实现版本管理,Git都能跟踪并记录在该目录中发生的所有更新。
现在我们已经知道什么是repository(缩写repo)了,假如我们现在建立一个仓库(repo),那么在建立仓库的这个目录中有一个&.git&的文件夹。这个文件夹非常重要,所有的版本信息,更新记录,以及Git进行仓库管理的相关信息全部保存在这个文件夹里面。所以,不要修改/删除其中的文件,以免造成数据的丢失。
进一步的讲解请参考下面一张图,大概展示出了我们需要了解的基本知识。
&根据上面的图片,下面给出了每个部分的简要说明:
Directory:使用Git管理的一个目录,也就是一个仓库,包含我们的工作空间和Git的管理空间。
WorkSpace:需要通过Git进行版本控制的目录和文件,这些目录和文件组成了工作空间。
.git:存放Git管理信息的目录,初始化仓库的时候自动创建。
Index/Stage:暂存区,或者叫待提交更新区,在提交进入repo之前,我们可以把所有的更新放在暂存区。
Local Repo:本地仓库,一个存放在本地的版本库;HEAD会只是当前的开发分支(branch)。
Stash:是一个工作状态保存栈,用于保存/恢复WorkSpace中的临时状态。
&有了上面概念的了解,下面就开始在本地repo上进行Git操作了。
通过&Git Bash&命令行窗口进入到想要建立版本仓库的目录,通过&git init&就可以轻松的建立一个仓库。
这时,我们的仓库中会自动的产生一个&.git&文件夹,这个就是我们前面提到的Git管理信息的目录。
现在我们在仓库中新建一个&test.txt&的文本文件,内容如下。
通过"git status"可以查看WorkSpace的状态,看到输出显示"test.txt"没有被Git跟踪,并且提示我们可以使用"git add &file&..."把该文件添加到待提交区(暂存区)。
注意,如果添加到暂存区,这时的更新只是在WorkSpace中。
使用"git add test.txt"或者"git add .",然后继续查看WorkSpace的状态。这时发现文件已经被放到暂存区。
这时的更新已经从WorkSpace保存到Stage中。
最后,我们就可以通过&git commit -m&来提交更新了。-m后面跟的是对commit的描述(message)。
这时的更新已经从Stage保存到了Local Repo中。
通过上面的操作,文件"test.txt"就成功的被添加到了仓库中。
假设现在需要对"test.txt"进行更新,修改文件后,查看WorkSpace的状态,会发现提示文件有更新,但是更新只是在WorkSpace中,没有到暂存区中。
莫语常言道知足,万事至终总是空。
理想现实一线隔,心无旁骛脚踏实。
谁无暴风劲雨时,守得云开见月明。
花开复见却飘零,残憾莫使今生留。
同样,通过add、commit的操作,我们可以把文件的更新先放到暂存区,然后从暂存区提交到repo中。
注意,只有被add到暂存区的更新才会被提交进入repo。提交前,如果对WorkSpace的文件进行修改,而没有被添加到暂存区,那么提交进repo中的只是暂存区的更新,WorkSpace修改的部分不会提交进repo中的。
"git diff"是个很有用,而且会经常用到的命令,用于显示WorkSpace中的文件和暂存区文件的差异。先将我们之前的test.txt文件中的内容改成纯数字然后提交到repo中,中文字符运行该命令会显示乱码。
接下来修改成下面内容,我们通过"git diff"可以查看WorkSpace和Stage的diff情况,当我们把更新add到Stage中,diff就不会有任何输出了。
当然,我们也可以把WorkSpace中的状态和repo中的状态进行diff,命令如下。
git diff HEAD~n
根据前面对基本概念的了解,更新可能存在三个地方,WorkSpace中,Stage中和repo中。下面就分别介绍一下怎么撤销这些更新。
撤销WorkSpace中的更新
接着上面的例子,先将test.txt中的文本设置如下内容,并提交。
然后将test.txt中的内容进行修改:
我们可以使用"git checkout --&file&..."来撤销WorkSpace中的更新。执行test.txt中的文本会变成两行"abc"。
注意:使用这种方法撤销更新的时候一定要慎重,因为通过这种方式撤销后,更新将没有办法再找回。
撤销Stage中的更新
由于上个步骤,我们将test.txt修改撤销了。这里再次修改为下面内容,并且使用了"git add"把这个更新提交到了暂存区。这时,"git status"的输出中提示我们可以通过"git reset HEAD &file&..."把暂存区的更新移出到WorkSpace中。
如果想继续撤销WorkSpace中的更新,请参考上面一步。
撤销repo中的更新
介绍撤销repo中的更新之前,我们先看一下"git log"这个命令,通过这个命令我们可以查看commit的历史记录。
撤销提交有两种方式:使用HEAD指针和使用commit id
在Git中,有一个HEAD指针指向当前分支中最新的提交。当前版本,我们使用"HEAD^",那么再钱一个版本可以使用"HEAD^^",如果想回退到更早的提交,可以使用"HEAD~n"。(也就是,HEAD^=HEAD~1,HEAD^^=HEAD~2)
git rest --hard HEAD^
git rest --hard HEAD~1
git rest --cbc67a8b990c1da508d40cca623f23
再次查看,发现最新的提交已经被撤销了,查看test.txt中的文件,发现内容又变成两行"abc"文本。
那么问题就来了,我现在又想恢复被撤销的提交,当然Git是支持这样的操作。
下面来看看"git reflog"这个命令。"git log"只是包括了当前分支中的commit记录,而"git reflog"中会记录这个仓库中所有的分支的所有更新记录,包括已经撤销的更新。
有了这个,我们就可以恢复撤销操作。
git reset --hard HEAD@{1}
git reset --hard f752570
再次查看,发现我们撤销的内容已经回来了。
--hard和--soft
前面在使用reset来撤销更新的时候,我们都是使用的"--head"选项,其实与之对应的还有一个"--soft"选项,区别如下:
--head:撤销并删除相应的更新
--soft:撤销相应的更新,把这些更新的内容放到Stage中
在Git中,如果我们要删除一个文件,可以使用下面的命令,"git rm"相比"rm"只是多了一步,把这次删除的更新发到Stage中。
git rm &file&
阅读(...) 评论()

我要回帖

更多关于 仓库管理制度 的文章

 

随机推荐