国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Docker學(xué)習(xí):Image的本地存儲(chǔ)結(jié)構(gòu)

Worktile / 3461人閱讀

摘要:內(nèi)容為一個(gè),指向本地的真正存儲(chǔ)位置。但是,因?yàn)樵撶R像只有一層,很多關(guān)聯(lián)關(guān)系并沒有很好的體現(xiàn),接下來(lái)用一個(gè)稍微復(fù)雜的鏡像再過一遍上述過程。

寫在前面

在使用Docker時(shí)候,針對(duì)鏡像的操作一般就是docker pulldocker builddocker commit(剛開始接觸Docker的時(shí)候,還不會(huì)Dockerfile,經(jīng)常使用這個(gè)命令,但是經(jīng)歷了一次血的教訓(xùn),我已經(jīng)放棄這個(gè)命令很久)這些操作,大概都知道Images在Docker中是由無(wú)數(shù)個(gè)Layer組成,但是,Image在本地是如何存儲(chǔ)的?上述操作又會(huì)對(duì)本地存儲(chǔ)帶來(lái)怎樣的變化?抱著學(xué)習(xí)的態(tài)度,我從剛剛安裝完docker開始,一步一步研究docker image的目錄結(jié)構(gòu)和含義。
本人也只是docker初學(xué)者,寫文章的目的也是希望自己不僅僅停留在會(huì)使用docker的階段,還能夠邊用邊學(xué)邊總結(jié),一方面加深自己的理解,另一方面希望通過這種方式與一起學(xué)習(xí)Docker的童鞋們交流。如有錯(cuò)誤,歡迎批評(píng)指正,謝謝。

背景:Image 大小無(wú)法壓縮引出的問題

以前基本都在本地服務(wù)器上使用Dockerfile構(gòu)建鏡像,一般來(lái)說(shuō)磁盤的空間都是足夠的,而且基本不需要docker save,應(yīng)用場(chǎng)景也不存在頻發(fā)啟動(dòng)容器的情況,所以不管是空間還是效率的角度,都沒有刻意去壓縮構(gòu)建出來(lái)的鏡像大小。但是,最近因?yàn)樾枰赩PS上構(gòu)建,可用的空間嚴(yán)重受限,因此,覺得重寫Dockerfile來(lái)壓縮鏡像大小。本以為應(yīng)該是一件很簡(jiǎn)單的事情,果然太年輕。直接從dockerfile說(shuō)起:

FROM alpine
........
RUN apk -U upgrade && 
    apk -v add --no-cache bash curl && 
    apk -v add --no-cache --virtual .build-deps gcc make && 
    apk -v add --no-cache mysql-client libc-dev mariadb-dev && 
    rm -rf /var/cache/apk/*
COPY ./startService.sh /
........
RUN make clean && make && make install && 
    apk del .build-deps
    
CMD ["/bin/bash", "/startService.sh"]

實(shí)驗(yàn)發(fā)現(xiàn)上面的寫法,apk del .build-deps這一句加不加,大小都是一樣的,也就是說(shuō)完全沒有像預(yù)期的一樣,卸載環(huán)境就可以壓縮大小。一通googole,問題得到了解決,大家給出來(lái)的原因基本可以總結(jié)為:“Image是由多個(gè)Layer組成的,后面的Layer沒辦法修改前面的Layer”,修改一下dockerfile就能解決:

FROM alpine
........
RUN apk -U upgrade && 
    apk -v add --no-cache bash curl && 
    apk -v add --no-cache mysql-client libc-dev mariadb-dev && 
    rm -rf /var/cache/apk/*
COPY ./startService.sh /
........
RUN apk -v add --no-cache --virtual .build-deps gcc make && 
    make clean && make && make install && 
    apk del .build-deps
    
CMD ["/bin/bash", "/startService.sh"]

問題確實(shí)解決了,也大概能體會(huì)到在dockerfile中,最好把中間過程寫在一起,減少Layer,但是,為什么會(huì)這樣?很明顯,從最開始的錯(cuò)誤理解和現(xiàn)在的不理解,都是因?yàn)閷?duì)Image的實(shí)現(xiàn)原理不清楚,所以,決定從Image本地目錄結(jié)構(gòu)的角度來(lái)分析和理解。如果只想看結(jié)論,直接跳到最后吧。

環(huán)境

Centos 7.4

Docker 18.09.0

因?yàn)椴煌腄ocker版本,目錄結(jié)構(gòu)有一些差異,下面的操作都是針對(duì)V18.09.0,而不同的操作系統(tǒng)會(huì)影響默認(rèn)的存儲(chǔ)方式等,這里使用的是Centos 7.4
接下來(lái)的內(nèi)容,首先根據(jù)最初始的Docker環(huán)境,拉去一個(gè)alpine鏡像分析本地目錄結(jié)構(gòu),以及每一個(gè)目錄或文件的含義;然后基于alpine鏡像,從dockerfile中構(gòu)建一個(gè)簡(jiǎn)單的test-image鏡像,完成構(gòu)建之后進(jìn)一步分析和驗(yàn)證目錄或文件的含義,并分析Image和Layer的關(guān)聯(lián)關(guān)系在本地文件系統(tǒng)是如何實(shí)現(xiàn)關(guān)聯(lián)的。

從最簡(jiǎn)單的docker pull alpine了解本地目錄結(jié)構(gòu)

一般默認(rèn)安裝啟動(dòng)Docker,所有相關(guān)的文件都會(huì)存儲(chǔ)在/var/lib/docker下面,可以使用tree /var/lib/docker 查看目錄結(jié)構(gòu),而與Image相關(guān)的目錄主要包括兩個(gè):imageoverlay2,需要注意overlay2,是存儲(chǔ)驅(qū)動(dòng),不同的操作系統(tǒng)和docker版本可能不太一致,所以在查看目錄的時(shí)候要結(jié)合自己的環(huán)境:

/var/lib/docker
├── builder
│?? └── fscache.db
├── buildkit
│?? ├── cache.db
│?? ├── content
│?? │?? └── ingest
│?? ├── executor
│?? ├── metadata.db
│?? └── snapshots.db
├── containerd
│?? └── daemon
│??     ├── ........
│??     └── tmpmounts
├── containers
├── image
│?? └── overlay2
│??     ├── distribution
│??     ├── imagedb
│??     │?? ├── content
│??     │?? │?? └── sha256
│??     │?? └── metadata
│??     │??     └── sha256
│??     ├── layerdb
│??     └── repositories.json
├── network
│?? └── files
│??     └── local-kv.db
├── overlay2
│?? ├── backingFsBlockDev
│?? └── l
├── plugins
│?? ├── storage
│?? │?? └── blobs
│?? │??     └── tmp
│?? └── tmp
├── runtimes
├── swarm
├── tmp
├── trust
└── volumes
    └── metadata.db

因?yàn)樯厦媸莿偘惭b完的狀態(tài),并沒有pull或者build任何鏡像,所以目前image目錄下只有一些默認(rèn)的文件或者目錄,而且文件和目錄也沒有存什么有用的信息。現(xiàn)在我們使用docker pull alpine獲取一個(gè)最簡(jiǎn)單的鏡像。

[root@docker-learn docker]# docker pull alpine
Using default tag: latest
latest: Pulling from library/alpine
cd784148e348: Pull complete 
Digest: sha256:46e71df1e5191ab8b8034c5189e325258ec44ea739bba1e5645cff83c9048ff1
Status: Downloaded newer image for alpine:latest

上面拉去過程只會(huì)產(chǎn)生一個(gè)Layer,我們可以通過docker images --digests命令查看拉取的鏡像,注意Image ID和digest的區(qū)別。

[root@docker-learn docker]# docker images --digests
REPOSITORY          TAG                 DIGEST                                                                    IMAGE ID            CREATED             SIZE
alpine              latest              sha256:46e71df1e5191ab8b8034c5189e325258ec44ea739bba1e5645cff83c9048ff1   3f53bb00af94        8 days ago          4.41MB

此時(shí),我們可以再看文件系統(tǒng)的變化,為了方便,只展示image目錄:

image/
└── overlay2
    ├── distribution
    │?? ├── diffid-by-digest
    │?? │?? └── sha256
    │?? │??     └── cd784148e3483c2c86c50a48e535302ab0288bebd587accf40b714fffd0646b3
    │?? └── v2metadata-by-diffid
    │??     └── sha256
    │??         └── 7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8
    ├── imagedb
    │?? ├── content
    │?? │?? └── sha256
    │?? │??     └── 3f53bb00af943dfdf815650be70c0fa7b426e56a66f5e3362b47a129d57d5991
    │?? └── metadata
    │??     └── sha256
    ├── layerdb
    │?? ├── sha256
    │?? │?? └── 7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8
    │?? │??     ├── cache-id
    │?? │??     ├── diff
    │?? │??     ├── size
    │?? │??     └── tar-split.json.gz
    │?? └── tmp
    └── repositories.json
repositories.json

這個(gè)文件存儲(chǔ)了本地的所有images列表,里面目前包含了兩個(gè),"alpine:latest""alpine@sha256:46e71df1e5191ab8b8034c5189e325258ec44ea739bba1e5645cff83c9048ff1",其實(shí)這兩個(gè)是同一個(gè)鏡像,你可以在剛剛docker images --digests看到,前者是tag,后者是digest(docker inspect 3f53bb00af94也可以看到相同的效果)。

{
    "Repositories": {
        "alpine": {
            "alpine:latest": "sha256:3f53bb00af943dfdf815650be70c0fa7b426e56a66f5e3362b47a129d57d5991",
            "alpine@sha256:46e71df1e5191ab8b8034c5189e325258ec44ea739bba1e5645cff83c9048ff1": "sha256:3f53bb00af943dfdf815650be70c0fa7b426e56a66f5e3362b47a129d57d5991"
        }
    }
}
imagedb目錄
imagedb/
├── content
│?? └── sha256
│??     └── 3f53bb00af9......
└── metadata
    └── sha256

該目錄存儲(chǔ)了鏡像的相關(guān)信息,每個(gè)鏡像的內(nèi)容都包含在自己的目錄下,目錄名即為該鏡像的Image ID
首先是metadata目錄,該目錄保存每個(gè)鏡像的parent鏡像ID,因?yàn)檫@里的alpine:lasted鏡像沒有更上層的鏡像,所以目錄為空,后續(xù)我們使用docker build構(gòu)建一個(gè)鏡像,再進(jìn)一步分析。
其次是content目錄,該目錄下存儲(chǔ)了鏡像的JSON格式描述信息:

{
    "architecture": "amd64",
    "config": {
        "ArgsEscaped": true,
        "AttachStderr": false,
        "AttachStdin": false,
        "AttachStdout": false,
        "Cmd": [
            "/bin/sh"
        ],
        "Domainname": "",
        "Entrypoint": null,
        "Env": [
            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
        ],
        "Hostname": "",
        "Image": "sha256:49573004c44f9413c7db63cbab336356e7a8843139fca5e68f92d84a56f0e6df",
        "Labels": null,
        "OnBuild": null,
        "OpenStdin": false,
        "StdinOnce": false,
        "Tty": false,
        "User": "",
        "Volumes": null,
        "WorkingDir": ""
    },
    "container": "c44d11fa67899a984d66f5542092b474f11ca95cc9b03b1470546f16ec8ce74f",
    "container_config": {
        "ArgsEscaped": true,
        "AttachStderr": false,
        "AttachStdin": false,
        "AttachStdout": false,
        "Cmd": [
            "/bin/sh",
            "-c",
            "#(nop) ",
            "CMD ["/bin/sh"]"
        ],
        "Domainname": "",
        "Entrypoint": null,
        "Env": [
            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
        ],
        "Hostname": "c44d11fa6789",
        "Image": "sha256:49573004c44f9413c7db63cbab336356e7a8843139fca5e68f92d84a56f0e6df",
        "Labels": {},
        "OnBuild": null,
        "OpenStdin": false,
        "StdinOnce": false,
        "Tty": false,
        "User": "",
        "Volumes": null,
        "WorkingDir": ""
    },
    "created": "2018-12-21T00:21:30.122610396Z",
    "docker_version": "18.06.1-ce",
    "history": [
        {
            "created": "2018-12-21T00:21:29.97055571Z",
            "created_by": "/bin/sh -c #(nop) ADD file:2ff00caea4e83dfade726ca47e3c795a1e9acb8ac24e392785c474ecf9a621f2 in / "
        },
        {
            "created": "2018-12-21T00:21:30.122610396Z",
            "created_by": "/bin/sh -c #(nop)  CMD ["/bin/sh"]",
            "empty_layer": true
        }
    ],
    "os": "linux",
    "rootfs": {
        "diff_ids": [
            "sha256:7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8"
        ],
        "type": "layers"
    }
}

解釋以下主要的幾個(gè)部分:

config: 未來(lái)根據(jù)這個(gè)image啟動(dòng)container時(shí),config里面的配置就是運(yùn)行container時(shí)的默認(rèn)參數(shù)。

container: 此處為一個(gè)容器ID,一般我們執(zhí)行docker build構(gòu)建鏡像時(shí),可以看見是不斷地生成新的container,然后提交為新的image,此處的容器ID即生成該鏡像時(shí)臨時(shí)容器的ID,后面通過docker build構(gòu)建鏡像會(huì)進(jìn)一步驗(yàn)證。

container_config:上述臨時(shí)容器的配置,可以對(duì)比containner_configconfig的內(nèi)容,字段完全一致,驗(yàn)證了config的作用。

history:構(gòu)建該鏡像的所有歷史命令

rootfs:該鏡像包含的layer層的diff id。

layerdb目錄

imagedb目錄一樣,根據(jù)命名即可理解該目錄主要用來(lái)存儲(chǔ)Docker的Layer信息,在只有一個(gè)alpine:lasted鏡像的情況下,目錄結(jié)構(gòu)如下:

layerdb/
├── sha256
│?? └── 7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8
│??     ├── cache-id
│??     ├── diff
│??     ├── size
│??     └── tar-split.json.gz
└── tmp

在我們docker pull alpine:lasted的時(shí)候,可以發(fā)現(xiàn)只pull了一層,而在上面imagedb/content中的鏡像信息中,rootfs中也只有一個(gè)diff,因此,與此處的一個(gè)Layer層吻合。但是,需要注意此處的 7bff100f35... 與rootfs中的diff_id 7bff100f35...雖然值一樣,但是含義并不相同,此處標(biāo)識(shí)Layer的Chain ID,之所以此處一致,是因?yàn)樵谥挥幸粚覮ayer,沒有parent時(shí),diff id與chain id相等,后面我們構(gòu)建test-image后再來(lái)分析即可看出區(qū)別。
在改Layer的目錄下,包含四個(gè)文件:

diff:該Layer層的diff id

[root@docker-learn overlay2]# cat layerdb/sha256/7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8/diff
sha256:7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8

如上面所述,最底層的Layer具有相同的chain id 和 diff id

size:該Layer的大小,單位字節(jié)

[root@docker-learn overlay2]# cat layerdb/sha256/7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8/size
4413428

在docker images中,我們可以看到alpine鏡像的大小為4.41MB,將此處的大小進(jìn)行換算 4413428/(1024*1024),發(fā)現(xiàn)大小不一致,第一反應(yīng)是Image相對(duì)于Layer還增加了其他信息,但是理論上似乎無(wú)法解釋,于是使用docker inspect alpine查看了鏡像的具體信息,發(fā)現(xiàn)其中Size: 4413428,與該處數(shù)值一直,那么4.41M應(yīng)該是4413428/(1000000)計(jì)算得來(lái),后面我們會(huì)使用test-image鏡像進(jìn)一步驗(yàn)證。

tar-split.json.gz:layer層數(shù)據(jù)tar壓縮包的split文件

該文件生成需要依賴tar-split,通過這個(gè)文件可以還原layer的tar包。

cache_id:內(nèi)容為一個(gè)uuid,指向Layer本地的真正存儲(chǔ)位置。

[root@docker-learn layerdb]# cat sha256/7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8/cache-id 
281c53a74496be2bfcf921ceee5ec2d95139c43fec165ab667a77899b3691d56

那么Layer本地真正的存儲(chǔ)位置又在何處呢?便是上面提到的/var/lib/docker/overlay2目錄下:

[root@docker-learn overlay2]# ls
281c53a74496be2bfcf921ceee5ec2d95139c43fec165ab667a77899b3691d56  backingFsBlockDev  l

需要注意,layerdb目錄下除了diff、size、cache_id和tar-split.json.gz文件,還應(yīng)該包括一個(gè)parent文件,文件存儲(chǔ)了當(dāng)前Layer層的父層chain_id,因?yàn)楫?dāng)前alpine鏡像只有一層,所以也就沒有parent。

distribution目錄

該目錄包含了Layer層diif id和digest之間的對(duì)應(yīng)關(guān)系。

[root@docker-learn overlay2]# tree distribution/
distribution/
├── diffid-by-digest
│?? └── sha256
│??     └── cd784148e3483c2c86c50a48e535302ab0288bebd587accf40b714fffd0646b3
└── v2metadata-by-diffid
    └── sha256
        └── 7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8

4 directories, 2 files

v2metadata-by-diffid目錄下,我們可以通過Layer的diff id找到對(duì)應(yīng)的digest,并且包含了生成該digest的源倉(cāng)庫(kù)。

[
    {
        "Digest": "sha256:cd784148e3483c2c86c50a48e535302ab0288bebd587accf40b714fffd0646b3",
        "HMAC": "",
        "SourceRepository": "docker.io/library/alpine"
    }
]

diffid-by-digest目錄則與v2metadata-by-diffid相反

[root@docker-learn overlay2]# cat distribution/diffid-by-digest/sha256/cd784148e3483c2c86c50a48e535302ab0288bebd587accf40b714fffd0646b3 
sha256:7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8

到這里為止,基于最簡(jiǎn)單的alpine鏡像,我們看到了Image的本地目錄結(jié)構(gòu),以及每一個(gè)目錄或文件大概的作用。但是,因?yàn)樵撶R像只有一層,很多關(guān)聯(lián)關(guān)系并沒有很好的體現(xiàn),接下來(lái)用一個(gè)稍微復(fù)雜的鏡像再過一遍上述過程。

基于docker build test-image進(jìn)一步理解目錄結(jié)構(gòu)

一個(gè)簡(jiǎn)單的dockerfile構(gòu)建test-image

FROM alpine

LABEL name="test-image"

RUN apk -v add --no-cache bash 
RUN apk -v add --no-cache curl
COPY ./startService.sh /

CMD ["/bin/bash", "/startService.sh"]

構(gòu)建過程輸出如下:

[root@docker-learn docker]# docker build -t test-image .
Sending build context to Docker daemon  3.072kB
Step 1/6 : FROM alpine
 ---> 3f53bb00af94
Step 2/6 : LABEL name="test-image"
 ---> Running in 3bd6320fc291
Removing intermediate container 3bd6320fc291
 ---> bb97dd1fb1a1
Step 3/6 : RUN apk -v add --no-cache bash
 ---> Running in f9987ff57ad7
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/community/x86_64/APKINDEX.tar.gz
(1/5) Installing ncurses-terminfo-base (6.1_p20180818-r1)
(2/5) Installing ncurses-terminfo (6.1_p20180818-r1)
(3/5) Installing ncurses-libs (6.1_p20180818-r1)
(4/5) Installing readline (7.0.003-r0)
(5/5) Installing bash (4.4.19-r1)
Executing bash-4.4.19-r1.post-install
Executing busybox-1.28.4-r2.trigger
OK: 18 packages, 136 dirs, 2877 files, 13 MiB
Removing intermediate container f9987ff57ad7
 ---> a5635f1b1d00
Step 4/6 : RUN apk -v add --no-cache curl
 ---> Running in c49fb2e4b311
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/community/x86_64/APKINDEX.tar.gz
(1/5) Installing ca-certificates (20171114-r3)
(2/5) Installing nghttp2-libs (1.32.0-r0)
(3/5) Installing libssh2 (1.8.0-r3)
(4/5) Installing libcurl (7.61.1-r1)
(5/5) Installing curl (7.61.1-r1)
Executing busybox-1.28.4-r2.trigger
Executing ca-certificates-20171114-r3.trigger
OK: 23 packages, 141 dirs, 3040 files, 15 MiB
Removing intermediate container c49fb2e4b311
 ---> 9156d1521a2f
Step 5/6 : COPY ./startService.sh /
 ---> 704626646baf
Step 6/6 : CMD ["/bin/bash", "/startService.sh"]
 ---> Running in 1c5e6e861264
Removing intermediate container 1c5e6e861264
 ---> 6cd0a66e83f1
Successfully built 6cd0a66e83f1
Successfully tagged test-image:latest

鏡像build過程可以理解為基于一個(gè)鏡像啟動(dòng)一個(gè)容器,在容器內(nèi)執(zhí)行Dockerfile里的一條命令,生成一個(gè)新的鏡像。根據(jù)上述的輸入,test-image的構(gòu)建過程可以表示為:

最終生成的test-image鏡像ID為 6cd0a66e83f1,我們從該鏡像開始,再一次分析本地目錄。首先查看鏡像的基本信息:

[root@docker-learn docker]# docker images --digests test-image
REPOSITORY          TAG                 DIGEST              IMAGE ID            CREATED             SIZE
test-image          latest                            6cd0a66e83f1        About an hour ago   9.88MB

如前面所述,digest是有docker repository生成,因?yàn)楸镜貥?gòu)建完之后并沒有推送至遠(yuǎn)程倉(cāng)庫(kù),所以為None。此時(shí),image目錄發(fā)生了如下變化:

image/
└── overlay2
    ├── distribution
    │?? ├── diffid-by-digest
    │?? │?? └── sha256
    │?? │??     └── cd784148e3483c2c86c50a48e535302ab0288bebd587accf40b714fffd0646b3
    │?? └── v2metadata-by-diffid
    │??     └── sha256
    │??         └── 7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8
    ├── imagedb
    │?? ├── content
    │?? │?? └── sha256
    │?? │??     ├── 3f53bb00af943dfdf815650be70c0fa7b426e56a66f5e3362b47a129d57d5991
    │?? │??     ├── 6cd0a66e83f133a2bad37103ed03f6480330fa3c469368eb5871320996d3b924
    │?? │??     ├── 704626646baf8bdea82da237819cded076a0852eb97dba2fc731569dd85ae836
    │?? │??     ├── 9156d1521a2fd50d972e1e1abc30d37df7c8e8f7825ca5955170f3b5441b3341
    │?? │??     ├── a5635f1b1d0078cd926f21ef3ed77b357aa899ac0c8bf80cae51c37129167e3a
    │?? │??     └── bb97dd1fb1a10b717655594950efb4605ff0d3f2f631feafc4558836c2b34c3c
    │?? └── metadata
    │??     └── sha256
    │??         ├── 6cd0a66e83f133a2bad37103ed03f6480330fa3c469368eb5871320996d3b924
    │??         │?? ├── lastUpdated
    │??         │?? └── parent
    │??         ├── 704626646baf8bdea82da237819cded076a0852eb97dba2fc731569dd85ae836
    │??         │?? └── parent
    │??         ├── 9156d1521a2fd50d972e1e1abc30d37df7c8e8f7825ca5955170f3b5441b3341
    │??         │?? └── parent
    │??         ├── a5635f1b1d0078cd926f21ef3ed77b357aa899ac0c8bf80cae51c37129167e3a
    │??         │?? └── parent
    │??         └── bb97dd1fb1a10b717655594950efb4605ff0d3f2f631feafc4558836c2b34c3c
    │??             └── parent
    ├── layerdb
    │?? ├── mounts
    │?? ├── sha256
    │?? │?? ├── 0e88764cdf90e8a5d6597b2d8e65b8f70e7b62982b0aee934195b54600320d47
    │?? │?? │?? ├── cache-id
    │?? │?? │?? ├── diff
    │?? │?? │?? ├── parent
    │?? │?? │?? ├── size
    │?? │?? │?? └── tar-split.json.gz
    │?? │?? ├── 7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8
    │?? │?? │?? ├── cache-id
    │?? │?? │?? ├── diff
    │?? │?? │?? ├── size
    │?? │?? │?? └── tar-split.json.gz
    │?? │?? ├── 80fe1abae43103e3be54ac2813114d1dea6fc91454a3369104b8dd6e2b1363f5
    │?? │?? │?? ├── cache-id
    │?? │?? │?? ├── diff
    │?? │?? │?? ├── parent
    │?? │?? │?? ├── size
    │?? │?? │?? └── tar-split.json.gz
    │?? │?? └── db7c15c2f03f63a658285a55edc0a0012ccd0033f4695d4b428b1b464637e655
    │?? │??     ├── cache-id
    │?? │??     ├── diff
    │?? │??     ├── parent
    │?? │??     ├── size
    │?? │??     └── tar-split.json.gz
    │?? └── tmp
    └── repositories.json

可以看到,相比于只有alpine鏡像的時(shí)候,在imagedb的content和metadata下,增加了build過程中產(chǎn)生的鏡像(鏡像ID能夠一一對(duì)應(yīng)),在layerdb下增加了三個(gè)Layer,目前還看不出來(lái)關(guān)聯(lián)關(guān)系,后續(xù)分析。

repositories.json
[root@docker-learn docker]# cat image/overlay2/repositories.json | python -m json.tool
{
    "Repositories": {
        "alpine": {
            "alpine:latest": "sha256:3f53bb00af943dfdf815650be70c0fa7b426e56a66f5e3362b47a129d57d5991",
            "alpine@sha256:46e71df1e5191ab8b8034c5189e325258ec44ea739bba1e5645cff83c9048ff1": "sha256:3f53bb00af943dfdf815650be70c0fa7b426e56a66f5e3362b47a129d57d5991"
        },
        "test-image": {
            "test-image:latest": "sha256:6cd0a66e83f133a2bad37103ed03f6480330fa3c469368eb5871320996d3b924"
        }
    }
}

可以看見,增加了test-image這一項(xiàng),包含其tag和id。

imagedb目錄
[root@docker-learn overlay2]# tree imagedb/
imagedb/
├── content
│?? └── sha256
│??     ├── 3f53bb00af943dfdf815650be70c0fa7b426e56a66f5e3362b47a129d57d5991
│??     ├── 6cd0a66e83f133a2bad37103ed03f6480330fa3c469368eb5871320996d3b924
│??     ├── 704626646baf8bdea82da237819cded076a0852eb97dba2fc731569dd85ae836
│??     ├── 9156d1521a2fd50d972e1e1abc30d37df7c8e8f7825ca5955170f3b5441b3341
│??     ├── a5635f1b1d0078cd926f21ef3ed77b357aa899ac0c8bf80cae51c37129167e3a
│??     └── bb97dd1fb1a10b717655594950efb4605ff0d3f2f631feafc4558836c2b34c3c
└── metadata
    └── sha256
        ├── 6cd0a66e83f133a2bad37103ed03f6480330fa3c469368eb5871320996d3b924
        │?? ├── lastUpdated
        │?? └── parent
        ├── 704626646baf8bdea82da237819cded076a0852eb97dba2fc731569dd85ae836
        │?? └── parent
        ├── 9156d1521a2fd50d972e1e1abc30d37df7c8e8f7825ca5955170f3b5441b3341
        │?? └── parent
        ├── a5635f1b1d0078cd926f21ef3ed77b357aa899ac0c8bf80cae51c37129167e3a
        │?? └── parent
        └── bb97dd1fb1a10b717655594950efb4605ff0d3f2f631feafc4558836c2b34c3c
            └── parent

9 directories, 12 files

在構(gòu)建test-image之前,metadata目錄為空,因?yàn)閍lpine沒有parent,在build之后,新增了5個(gè)目錄,分別對(duì)應(yīng)docker build test-image所產(chǎn)生的5個(gè)鏡像,每一層以上一層為parent,即parent文件中存儲(chǔ)了上一層image id。
content目錄中,相比于構(gòu)建test-image之前,也多了上述5個(gè)鏡像的內(nèi)容,以test-image為例(目前的最底層鏡像),查看其描述信息:

{
    "architecture": "amd64",
    "config": {
        "ArgsEscaped": true,
        "AttachStderr": false,
        "AttachStdin": false,
        "AttachStdout": false,
        "Cmd": [
            "/bin/bash",
            "/startService.sh"
        ],
        "Domainname": "",
        "Entrypoint": null,
        "Env": [
            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
        ],
        "Hostname": "",
        "Image": "sha256:704626646baf8bdea82da237819cded076a0852eb97dba2fc731569dd85ae836",
        "Labels": {
            "name": "test-image"
        },
        "OnBuild": null,
        "OpenStdin": false,
        "StdinOnce": false,
        "Tty": false,
        "User": "",
        "Volumes": null,
        "WorkingDir": ""
    },
    "container": "1c5e6e861264654f79a190eba5157dd4dedce59ab3de098a3625fb4e5b6f1d98",
    "container_config": {
        "ArgsEscaped": true,
        "AttachStderr": false,
        "AttachStdin": false,
        "AttachStdout": false,
        "Cmd": [
            "/bin/sh",
            "-c",
            "#(nop) ",
            "CMD ["/bin/bash" "/startService.sh"]"
        ],
        "Domainname": "",
        "Entrypoint": null,
        "Env": [
            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
        ],
        "Hostname": "1c5e6e861264",
        "Image": "sha256:704626646baf8bdea82da237819cded076a0852eb97dba2fc731569dd85ae836",
        "Labels": {
            "name": "test-image"
        },
        "OnBuild": null,
        "OpenStdin": false,
        "StdinOnce": false,
        "Tty": false,
        "User": "",
        "Volumes": null,
        "WorkingDir": ""
    },
    "created": "2019-01-01T02:29:19.701494089Z",
    "docker_version": "18.09.0",
    "history": [
        {
            "created": "2018-12-21T00:21:29.97055571Z",
            "created_by": "/bin/sh -c #(nop) ADD file:2ff00caea4e83dfade726ca47e3c795a1e9acb8ac24e392785c474ecf9a621f2 in / "
        },
        {
            "created": "2018-12-21T00:21:30.122610396Z",
            "created_by": "/bin/sh -c #(nop)  CMD ["/bin/sh"]",
            "empty_layer": true
        },
        {
            "created": "2019-01-01T02:29:06.530296297Z",
            "created_by": "/bin/sh -c #(nop)  LABEL name=test-image",
            "empty_layer": true
        },
        {
            "created": "2019-01-01T02:29:14.182236016Z",
            "created_by": "/bin/sh -c apk -v add --no-cache bash"
        },
        {
            "created": "2019-01-01T02:29:19.327280058Z",
            "created_by": "/bin/sh -c apk -v add --no-cache curl"
        },
        {
            "created": "2019-01-01T02:29:19.549474383Z",
            "created_by": "/bin/sh -c #(nop) COPY file:fff66db7f2d773b25215edcc9d5697d84813835e3b731e5a6afe9a9b9647ecec in / "
        },
        {
            "created": "2019-01-01T02:29:19.701494089Z",
            "created_by": "/bin/sh -c #(nop)  CMD ["/bin/bash" "/startService.sh"]",
            "empty_layer": true
        }
    ],
    "os": "linux",
    "rootfs": {
        "diff_ids": [
            "sha256:7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8",
            "sha256:b1ddbff022577cd249a074285a1a7eb76d7c9139132ba5aa4272fc115dfa9e36",
            "sha256:9edc93f4dcf640f272ed73f933863dbefae6719745093d09c6c6908f402b1c34",
            "sha256:a6c8828ba4b58628284f783d3c918ac379ae2aba0830f4c926a330842361ffb6"
        ],
        "type": "layers"
    }
}

主要注意一下幾個(gè)參數(shù):

container: 這里的容器ID可以與圖XX中生成該image的容器ID對(duì)應(yīng)上。

container_config:容器配置狀態(tài),可以看出是執(zhí)行完dockerfile中命令之后的容器狀態(tài)。

rootfs:鏡像包含的Layer的diff id,可以看出test-image鏡像包含了四個(gè)Layer。當(dāng)我第一次分析這里的時(shí)候,有一點(diǎn)點(diǎn)困惑,在我想象中Dockerfile中的每一條命令對(duì)應(yīng)一個(gè)Layer,也就是一個(gè)diff id,但是在dockerfile中包含6條命令,這里卻只有四層,進(jìn)一步整理并分析每一個(gè)image的rootfs,如下圖2所示。可以看到,在 LABEL name="test-image"CMD ["/bin/bash", "/startService.sh"]這兩行并未產(chǎn)生新的Layer。事實(shí)上,如果我們認(rèn)為鏡像是一個(gè)打包的靜態(tài)OS,那么Layer可以認(rèn)為是描述該OS的fs變化,即文件系統(tǒng)中文件或者目錄發(fā)生的改變,很明顯上述兩行命令并不會(huì)引起fs的變化,只是會(huì)寫入該鏡像的config中,在生成容器時(shí)讀取即可,自然也就不存在diff id。

至此,解釋完了Image相關(guān)的目錄,總結(jié)一下,單個(gè)Image的配置信息在content目錄中,以image id為文件名存儲(chǔ),Image之間關(guān)聯(lián)信息在metadata中,以parent文件存儲(chǔ)。然后,我們根據(jù)image生成容器的時(shí)候,可是生成了一個(gè)文件系統(tǒng),但是上述這些信息并不包含fs的數(shù)據(jù)。因?yàn)檎嬲膄s數(shù)據(jù)是存儲(chǔ)在Layer中的。如前面所述,Layer的信息存儲(chǔ)在layerdb目錄下,所以我們轉(zhuǎn)戰(zhàn)layerdb目錄。

layerdb目錄
[root@docker-learn overlay2]# tree layerdb/
layerdb/
├── mounts
├── sha256
│?? ├── 0e88764cdf90e8a5d6597b2d8e65b8f70e7b62982b0aee934195b54600320d47
│?? │?? ├── cache-id
│?? │?? ├── diff
│?? │?? ├── parent
│?? │?? ├── size
│?? │?? └── tar-split.json.gz
│?? ├── 7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8
│?? │?? ├── cache-id
│?? │?? ├── diff
│?? │?? ├── size
│?? │?? └── tar-split.json.gz
│?? ├── 80fe1abae43103e3be54ac2813114d1dea6fc91454a3369104b8dd6e2b1363f5
│?? │?? ├── cache-id
│?? │?? ├── diff
│?? │?? ├── parent
│?? │?? ├── size
│?? │?? └── tar-split.json.gz
│?? └── db7c15c2f03f63a658285a55edc0a0012ccd0033f4695d4b428b1b464637e655
│??     ├── cache-id
│??     ├── diff
│??     ├── parent
│??     ├── size
│??     └── tar-split.json.gz
└── tmp

相比于只有alpine鏡像時(shí),首先,layerdb目錄多了一個(gè)mounts目錄,簡(jiǎn)單來(lái)說(shuō),當(dāng)由鏡像生成容器時(shí),該目錄下會(huì)生成容器的可讀可寫兩個(gè)layer,可讀即為由鏡像生成,而可寫就是未來(lái)對(duì)容器的修改都會(huì)放在著了,因?yàn)楸疚闹挥懻撶R像,這個(gè)目錄就不再深入分析。

其次,目前Layer已經(jīng)增加至4個(gè),這與我們上一節(jié)中看到的test-image的rootfs配置中有4個(gè)layer diif id能夠?qū)?yīng)上,然后,很顯然除了第一層的"7bff100f35cb"能對(duì)應(yīng)上,其他三個(gè)完全不同。進(jìn)一步研究才知道這里目錄名其實(shí)是layer的chain id,而非diff id,關(guān)于這兩這個(gè)的區(qū)別,我們可以理解為diff id用來(lái)描述單個(gè)變化,而chain id用來(lái)便于一些列的變化,diff id和chain id之間的計(jì)算公式可以在image-spec中看到。

ChainID(A) = DiffID(A)
ChainID(A|B) = Digest(ChainID(A) + " " + DiffID(B))
ChainID(A|B|C) = Digest(ChainID(A|B) + " " + DiffID(C))

在這里以test-image的rootfs驗(yàn)證分析他們是如何關(guān)聯(lián)的。

"rootfs": {
    "diff_ids": [
        "sha256:7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8",
        "sha256:b1ddbff022577cd249a074285a1a7eb76d7c9139132ba5aa4272fc115dfa9e36",
        "sha256:9edc93f4dcf640f272ed73f933863dbefae6719745093d09c6c6908f402b1c34",
        "sha256:a6c8828ba4b58628284f783d3c918ac379ae2aba0830f4c926a330842361ffb6"
    ],
    "type": "layers"
}
ChainID(A) = DiffID(A) = sha256:7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8

ChainID(A|B) = Digest(ChainID(A) + " " + DiffID(B))
ChainID(A) = sha256:7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8
DiffID(B) = sha256:b1ddbff022577cd249a074285a1a7eb76d7c9139132ba5aa4272fc115dfa9e36
計(jì)算:
[root@docker-learn overlay2]# echo -n "sha256:7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8 sha256:b1ddbff022577cd249a074285a1a7eb76d7c9139132ba5aa4272fc115dfa9e36" | sha256sum -
db7c15c2f03f63a658285a55edc0a0012ccd0033f4695d4b428b1b464637e655  -

結(jié)果:
ChainID(A|B) = sha256:db7c15c2f03f63a658285a55edc0a0012ccd0033f4695d4b428b1b464637e655
chainID(A|B|C) = sha256:0e88764cdf90e8a5d6597b2d8e65b8f70e7b62982b0aee934195b54600320d47
chainID(A|B|C|D) = sha256:80fe1abae43103e3be54ac2813114d1dea6fc91454a3369104b8dd6e2b1363f5

][3]

因此,test-image的第二層Layer對(duì)應(yīng)的目錄為:layerdb/sha256/db7c15c2f03f63a658285a55edc0a0012ccd0033f4695d4b428b1b464637e655,查看該Layer的信息:

[root@docker-learn sha256]# ls db7c15c2f03f63a658285a55edc0a0012ccd0033f4695d4b428b1b464637e655/
cache-id  diff  parent  size  tar-split.json.gz

與上一節(jié)中相比多了parent,包含了上一層Layer的chain id。

destribution目錄
[root@docker-learn overlay2]# tree distribution/
distribution/
├── diffid-by-digest
│   └── sha256
│       └── cd784148e3483c2c86c50a48e535302ab0288bebd587accf40b714fffd0646b3
└── v2metadata-by-diffid
    └── sha256
        └── 7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8

4 directories, 2 files

目前來(lái)看,destribution目錄和只有alpine鏡像時(shí)并沒有什么區(qū)別,這是因?yàn)閐igest是由鏡像倉(cāng)庫(kù)生成,本地構(gòu)建的鏡像在沒有push到倉(cāng)庫(kù)之前,自然也就沒有digest。使用docker push命令將test-image推送至dockerhub。

[root@docker-learn distribution]# docker push backbp/test-image:lasted
The push refers to repository [docker.io/backbp/test-image]
a6c8828ba4b5: Pushed 
9edc93f4dcf6: Pushed 
b1ddbff02257: Pushed 
7bff100f35cb: Mounted from library/alpine 
lasted: digest: sha256:3dc66a43c28ea3e994e4abf6a2d04c7027a9330e8eeab5c609e4971a8c58f0b0 size: 1156

根據(jù)過程輸出,我們可以看到雖然test-image鏡像包括四層Layer,但是因?yàn)樽畹讓拥?bff100f35cb本來(lái)就是在docker pull alpine時(shí),拉取得來(lái),自然也就不需要再push,因此真正push的只有三層。而現(xiàn)在destribution目錄也已經(jīng)增加了對(duì)應(yīng)Layer的digest,Image的digest可以在上面的過程輸出中看到。

distribution/
├── diffid-by-digest
│?? └── sha256
│??     ├── 2826782ee82560ec5f90a8a9da80880d48dd4036763f5250024fab5b3ef8e8cf
│??     ├── 8e905c02e6908fbb0e591cea285470208920d32408735bd6a8fcaf85ffba9089
│??     ├── a5bec9983f6902f4901b38735db9c427190ffcb3734c84ee233ea391da81081b
│??     └── cd784148e3483c2c86c50a48e535302ab0288bebd587accf40b714fffd0646b3
└── v2metadata-by-diffid
    └── sha256
        ├── 7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8
        ├── 9edc93f4dcf640f272ed73f933863dbefae6719745093d09c6c6908f402b1c34
        ├── a6c8828ba4b58628284f783d3c918ac379ae2aba0830f4c926a330842361ffb6
        └── b1ddbff022577cd249a074285a1a7eb76d7c9139132ba5aa4272fc115dfa9e36
拓展思考 docker tag命令發(fā)生了什么?

我們通過基于test-image生成一個(gè)新的tag,test-image-tag

[root@docker-learn overlay2]# docker tag test-image test-image-tag
[root@docker-learn overlay2]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
test-image-tag      latest              6cd0a66e83f1        4 hours ago         9.88MB
test-image          latest              6cd0a66e83f1        4 hours ago         9.88MB
alpine              latest              3f53bb00af94        11 days ago         4.41MB

查看repositories.json文件會(huì)發(fā)現(xiàn)兩個(gè)tag的鏡像都指向同一個(gè)image id,所以這個(gè)命令相當(dāng)于只有修改了repositories.json。

[root@docker-learn overlay2]# cat repositories.json | python -m json.tool
{
    "Repositories": {
        "alpine": {
            "alpine:latest": "sha256:3f53bb00af943dfdf815650be70c0fa7b426e56a66f5e3362b47a129d57d5991",
            "alpine@sha256:46e71df1e5191ab8b8034c5189e325258ec44ea739bba1e5645cff83c9048ff1": "sha256:3f53bb00af943dfdf815650be70c0fa7b426e56a66f5e3362b47a129d57d5991"
        },
        "test-image": {
            "test-image:latest": "sha256:6cd0a66e83f133a2bad37103ed03f6480330fa3c469368eb5871320996d3b924"
        },
        "test-image-tag": {
            "test-image-tag:latest": "sha256:6cd0a66e83f133a2bad37103ed03f6480330fa3c469368eb5871320996d3b924"
        }
    }
}
Image Size是如何計(jì)算的?

上述的alpine鏡像和test-image鏡像大小分別是:4.41M和9.88M。為了更準(zhǔn)確的分析,可以使用docker inspect 鏡像查看詳細(xì)大小,分別為4413428和9876099。
再次查看每層Layer的大小(layerdb/layer chain id/size),分別為

Layer1:4413428

Layer2:3815516

Layer3:1647117

Layer4:38

alpine只有一層,所以image size與Layer size相等;test-image有四層,所以image size = sum(Layer1+Layer2+Layer3+Layer4)=9876099

回到最初的問題

完成上面的分析,現(xiàn)在再回頭看最初的問題,在apk del .build-deps這一層中,執(zhí)行完之后生成的Layer只是記錄相對(duì)于上一層刪除了安裝包,在計(jì)算Image size時(shí)(或者說(shuō)在根據(jù)Layer合并Image時(shí)),因?yàn)榘惭b.build-deps而帶來(lái)的變換依然存在于app add這一層Layer中,所以大小并不會(huì)減小。如果將add和del放在同一個(gè)命令內(nèi),那么生成的該層Layer記錄的是相對(duì)于上一層的變化,.build-deps的安裝和卸載相對(duì)于上一層而言,根本就不存在,所以Layer中也就完全不存在。
說(shuō)到底,還是OCI Image的原理所致,最需要記住的是每一個(gè)Layer記錄的是與上一層Layer相比的變化。

參考

image-spec

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/27645.html

相關(guān)文章

  • Docker學(xué)習(xí)筆記01-概述

    摘要:你甚至可以運(yùn)行自己的私有注冊(cè)表。當(dāng)你使用或命令時(shí),所需的鏡像將從配置的注冊(cè)表中拉取。本節(jié)簡(jiǎn)要概述其中的一些對(duì)象。這允許運(yùn)行的容器在其本地文件系統(tǒng)中創(chuàng)建或修改文件和目錄。啟動(dòng)容器并執(zhí)行開啟容器內(nèi)的終端。輸入以終止命令,容器停止,但未被刪除。 Docker是什么 Docker是一個(gè)開源的應(yīng)用容器引擎,讓開發(fā)者可以打包他們的應(yīng)用以及依賴包到一個(gè)可移植的容器中,然后發(fā)布到任何流行的Linux機(jī)...

    weakish 評(píng)論0 收藏0
  • Docker學(xué)習(xí)之路(五)鏡像基本操作

    摘要:一個(gè)鏡像可以放到另一個(gè)京廣線的頂部,位于下面的鏡像稱為父鏡像,最底部的稱為基礎(chǔ)鏡像。鏡像是基于聯(lián)合文件系統(tǒng)的一種層式的結(jié)構(gòu),由一系列指令一步步構(gòu)建處理。拉取鏡像使用命令啟動(dòng)一個(gè)鏡像時(shí),會(huì)檢查本地是否存在該鏡像。 什么是鏡像 Docker鏡像時(shí)由文件系統(tǒng)疊加而成,最底端是一個(gè)引導(dǎo)文件系統(tǒng),即bootfs,這很像典型的Linux/Unix的引導(dǎo)文件系統(tǒng)。Docker用戶幾乎永遠(yuǎn)不會(huì)和引導(dǎo)...

    yunhao 評(píng)論0 收藏0
  • 都9102年了,還不會(huì)Docker?10分鐘帶你從入門操作到實(shí)戰(zhàn)上手

    摘要:聯(lián)調(diào)測(cè)試,無(wú)需依賴他人。針對(duì)以上問題,有兩種解決方法,一個(gè)是自己搭建私有服務(wù),另一個(gè)是用云服務(wù)的鏡像管理平臺(tái)如阿里云的容器鏡像服務(wù)。利用,先對(duì)阿里云的服務(wù)進(jìn)行登錄。推送后,就能在阿里云的倉(cāng)庫(kù)上看到這個(gè)鏡像。 Docker簡(jiǎn)述 Docker是一種OS虛擬化技術(shù),是一個(gè)開源的應(yīng)用容器引擎。它可以讓開發(fā)者將應(yīng)用打包到一個(gè)可移植的容器中,并且該容器可以運(yùn)行在幾乎所有l(wèi)inux系統(tǒng)中(Windo...

    sf_wangchong 評(píng)論0 收藏0
  • 進(jìn)入docker世界

    摘要:反過來(lái)別的上的鏡像,也不能在樹莓派上運(yùn)行。如果需要找樹莓派專用的鏡像,那就在上搜索或相關(guān)就能找到了。有一個(gè)叫的倉(cāng)庫(kù)制作了非常多樹莓派專用,可以參考下。樹莓派安裝,最難的在于正確的選擇源和添加,才能找到版本適合的并下載。 最近學(xué)習(xí)Machine Learning發(fā)現(xiàn)好多人都用docker,之前一直聽說(shuō)但是感覺和自己無(wú)關(guān)。但是現(xiàn)在發(fā)現(xiàn)原來(lái)docker是個(gè)這么方便的東西,可以跨平臺(tái)(不分什么...

    remcarpediem 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<