Docker學習筆記(一)


Docker學習筆記(一)

暑期加入了沃天宇老師的實驗室進行暑期的實習。在正式開始工作之前,師兄先讓我了解一下技術棧,需要了解的有docker、k8s、springboot、springcloud。

謹以一系列博客記錄一下自己學習的筆記。更多內容見Github

2021/7/5

主要內容為跟隨官方文檔的Get-Start過一遍流程,加上一些自己的探索和思考。

參考資料

什么是docker?

在開始學習之前,先連帶猜測給docker下一個定義,深入學習之后再回頭來驗證一下。

docker的作用應當是解決程序的環境問題,通過將環境和程序一起打包,使得能夠方便地在不同計算機上恢復程序所需要的環境並運行程序。

這將解決開發過程中的環境不一致問題,大大降低部署時的環境問題。

相比於虛擬機,docker應當具有較高的效率和較低的代價。

安裝

Linux參考:https://docs.docker.com/engine/install/

Windows參考:https://docs.docker.com/docker-for-windows/install/

注意Windows安裝的時候需要打開WSL2功能,並且升級至最新版本。

運行官方demo

下載

首先,下載官方的示例app,將倉庫中多余部分刪除,保留app文件夾,並重命名為gov-sample-app

Dockerfile

創建Dockerfile:

# syntax=docker/dockerfile:1
FROM node:12-alpine
RUN apk add --no-cache python g++ make
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]
  1. 第一句,FROM語句制定了初始的image,由於本地沒有這個image,所以會從倉庫中下載;
  2. 第二句,RUN,應當是指定了一些需要使用的包,到這一步應當也是下載安裝,作用應當是作為第一句引入的image的補充,添補額外的應用;
  3. 第三句,看起來應當是指定一個運行目錄,但是這個目錄是容器內的目錄還是

Build

然后切換到gov-sample-app文件夾下運行:

docker build -t getting-started .

這個命令通過剛剛創建的Dockerfile來創建一個docker鏡像。其中-t指令為該鏡像指定一個tag,我們運行這個鏡像的時候可以通過這個tag來指代這個鏡像。

此時先開始下載所需要的依賴,包括node:12-alpine鏡像和后面的python g++ make。但是此時發現下載速度感人,需要進行換源。

因為我是在自己的阿里雲服務器上進行實驗,所以換了阿里的源https://cr.console.aliyun.com/cn-beijing/instances/mirrors

重試發現仍不奏效,應當是第二步中apk add的問題。簡單搜索之后了解到,這里使用的鏡像Alpine其實是一個Linux發行版,因為其具有輕量化的優點,所以經常作為docker容器中使用的OS。因此,造成目前卡頓的原因是Alpine的包管理工具apk從倉庫下載速度過慢,應當對apk進行換源。

在第一句FROM后再加一行以進行換源:

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories 

雖然還是有些慢,但是好多了。最后Build成功,以下為輸出:

第一次Biuld:

$ sudo docker build -t getting-started .
Sending build context to Docker daemon  4.659MB
Step 1/7 : FROM node:12-alpine
 ---> deeae3752431
Step 2/7 : RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
 ---> Running in bb1b2b2fca8d
Removing intermediate container bb1b2b2fca8d
 ---> 306dcbb9fb1b
Step 3/7 : RUN apk add --no-cache python g++ make
 ---> Running in b9c56ec12a6b
fetch http://mirrors.aliyun.com/alpine/v3.11/main/x86_64/APKINDEX.tar.gz
fetch http://mirrors.aliyun.com/alpine/v3.11/community/x86_64/APKINDEX.tar.gz
(1/21) Installing binutils (2.33.1-r1)
(2/21) Installing gmp (6.1.2-r1)
(3/21) Installing isl (0.18-r0)
(4/21) Installing libgomp (9.3.0-r0)
(5/21) Installing libatomic (9.3.0-r0)
(6/21) Installing mpfr4 (4.0.2-r1)
(7/21) Installing mpc1 (1.1.0-r1)
(8/21) Installing gcc (9.3.0-r0)
(9/21) Installing musl-dev (1.1.24-r3)
(10/21) Installing libc-dev (0.7.2-r0)
(11/21) Installing g++ (9.3.0-r0)
(12/21) Installing make (4.2.1-r2)
(13/21) Installing libbz2 (1.0.8-r1)
(14/21) Installing expat (2.2.9-r1)
(15/21) Installing libffi (3.2.1-r6)
(16/21) Installing gdbm (1.13-r1)
(17/21) Installing ncurses-terminfo-base (6.1_p20200118-r4)
(18/21) Installing ncurses-libs (6.1_p20200118-r4)
(19/21) Installing readline (8.0.1-r0)
(20/21) Installing sqlite-libs (3.30.1-r2)
(21/21) Installing python2 (2.7.18-r0)
Executing busybox-1.31.1-r10.trigger
OK: 212 MiB in 37 packages
Removing intermediate container b9c56ec12a6b
 ---> 30167547cd72
Step 4/7 : WORKDIR /app
 ---> Running in 28935dd6ee9b
Removing intermediate container 28935dd6ee9b
 ---> 9b4f5c2f993f
Step 5/7 : COPY . .
 ---> 9e6b8013cc6f
Step 6/7 : RUN yarn install --production
 ---> Running in ffcc047905f0
yarn install v1.22.5
[1/4] Resolving packages...
[2/4] Fetching packages...
info There appears to be trouble with your network connection. Retrying...
info fsevents@1.2.9: The platform "linux" is incompatible with this module.
info "fsevents@1.2.9" is an optional dependency and failed compatibility check. Excluding it from installation.
[3/4] Linking dependencies...
[4/4] Building fresh packages...
Done in 93.45s.
Removing intermediate container ffcc047905f0
 ---> 4f85bb916dc2
Step 7/7 : CMD ["node", "src/index.js"]
 ---> Running in 05444490086d
Removing intermediate container 05444490086d
 ---> 302e7c6b161a
Successfully built 302e7c6b161a
Successfully tagged getting-started:latest

到此,我們知道了,docker是需要一個os的,而一個常用的選擇就是輕量的Linux發行版Alpine。我們通過Dockerfile來指定一個鏡像如何打包,其中FROM指令來指定一個初始的鏡像,類似於OOP中的繼承,我們在這個鏡像的基礎上,通過一系列指令來豐富其內容,創建我們的鏡像。

但是,仍舊有遺留的問題:

  1. RUNCMD有什么區別,它們的參數看起來都是在Alpine中運行的指令,這里的示例中它們采用了不同的格式,前者為shell格式,后者為exec格式,但是我試驗后發現,將其格式對調,沒有任何報錯;
  2. COPY到底拷貝了什么;
  3. WORKDIR是哪兒來的?目前來看,官方包里的示例代碼就是在app文件夾下,但是我這里將其改名為gov-sample-app,但是Build階段並沒有報錯;
  4. Build結束后應當有一個image,但是我並沒有在文件夾中看到新的文件,這個鏡像保存在哪里了?
  5. 在試驗遺留問題(1)的時候,我發現此時執行Build時,讓我之前頭疼的apk add的過程被跳過了,直接Using cache,並且可以發現,我只改動了Step6和7,對於前面的5步,除FROMCOPY外,都直接Using cache了(從windows的log來看,FROM也直接使用了本地的cache中的內容),看來docker似乎是對Dockerfile中的大部分步驟都一步一步地進行了cache,這是怎么實現的?

第二次Build:

sudo docker build -t getting-started .
Sending build context to Docker daemon  4.659MB
Step 1/7 : FROM node:12-alpine
 ---> deeae3752431
Step 2/7 : RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
 ---> Using cache
 ---> 306dcbb9fb1b
Step 3/7 : RUN apk add --no-cache python g++ make
 ---> Using cache
 ---> 30167547cd72
Step 4/7 : WORKDIR /app
 ---> Using cache
 ---> 9b4f5c2f993f
Step 5/7 : COPY . .
 ---> fb639ed69103
Step 6/7 : RUN ["yarn", "install", "--production"]
 ---> Running in 3bd8d313084f
yarn install v1.22.5
[1/4] Resolving packages...
[2/4] Fetching packages...
info fsevents@1.2.9: The platform "linux" is incompatible with this module.
info "fsevents@1.2.9" is an optional dependency and failed compatibility check. Excluding it from installation.
[3/4] Linking dependencies...
[4/4] Building fresh packages...
Done in 61.84s.
Removing intermediate container 3bd8d313084f
 ---> 9435838e03d2
Step 7/7 : CMD node src/index.js
 ---> Running in 49c60121400a
Removing intermediate container 49c60121400a
 ---> 7cb5bd6154b2
Successfully built 7cb5bd6154b2
Successfully tagged getting-started:latest

Run

docker run -dp 3000:3000 getting-started

其中-d指令表示這個鏡像放到后台去執行,此時不會進入到容器中,而是會直接detached,類似於screen的detached。

-p表示要指定端口的映射,后面3000:3000表示將宿主機的3000端口映射到容器的3000端口。經過試驗,前面的是宿主機的端口號,后面的是容器的端口號。

最后,通過build時指定的tag名稱來指定了需要運行的鏡像的名字——getting-started。可見,build打包好的鏡像應該是保存在了一個docker的倉庫里的,我們可以通過構建時指定的標簽來指定它。

訪問3000端口即可看到這個程序已經啟動起來了:

通過docker ps命令可以查看已經啟動的容器,然后通過docker stop <CONTAINER ID>來關閉已經detached的容器。

更新源碼

按照官方文檔,將src/static/js/app.js文件第56行進行修改:

 -                <p className="text-center">No items yet! Add one above!</p>
 +                <p className="text-center">You have no todo items yet! Add one above!</p>

此時再執行build,查看3000端口,發現更改沒有生效。再執行run,獲得如下報錯:

docker: Error response from daemon: driver failed programming external connectivity on endpoint hardcore_colden (b2a0229cf98dca6b7a0f58a654b8263ee6e4718629116598128d1a2c633b733e): Bind for 0.0.0.0:3000 failed: port is already allocated.

報錯顯示宿主機的3000端口已經被占用,改用3001:

docker run -dp 3001:3000 getting-started

可以看到更改生效了,而此時訪問3000仍舊可以看到舊的未更改的app。

此時,我們有以下這些命令:

構建一個鏡像:

docker build -t <NAME>

運行一個鏡像:

docker run -dp <HOST_PORT>:<CONTAINER_PORT> <NAME>

停止一個容器(但這個容器仍舊存在):

docker stop <CONTAINER>

刪除一個容器(必須已停止):

docker rm <CONTAINER>

停止並刪除一個容器:

docker rm -r <CONTAINER>

查看正在運行的:

docker ps

查看所有容器(包括已停止但未刪除的):

docker ps -a

指定<CONTAINER>的方式有三種:

  1. CONTAINER ID的全稱;
  2. CONTAINER ID的前綴子串,長度只要能夠區分不同的容器即可;
  3. NAMES,即容器的名字;

目前看來我已經啟動的容器都有一個自然語言的NAME,但是目前不知道這個是怎么指定的,也不知道怎么生成的。

分享/發布鏡像

首先,注冊一個DockerHub賬號,登錄並創建一個新的倉庫:

然后通過

docker login

來登錄自己的DockerHub賬號。在通過

docker tag getting-started <USER_ID>/getting-started

來創建一個應該與DockerHub對應的鏡像,這個鏡像是getting-started的復制。再通過

docker push <USER_ID>/getting-started

來將本地的鏡像同步至DockerHub倉庫。然后在另一台機器上(我在阿里雲的自己的服務器上)登錄后執行

docker pull <USER_ID>/getting-started

來將遠程倉庫的鏡像同步下來。這一系列操作與git差不多,只不過git可以通過git push -u來創建本地與遠程倉庫的關聯,而docker通過docker tag來創建一個鏡像的復制表示對遠程倉庫的引用。

最后在服務器上執行run,來啟動容器運行同步下來的進項。並且通過ssh的端口轉發實現訪問。

這里我將從遠程倉庫同步下來的鏡像啟動在3001端口,將未修改源碼時build的鏡像啟動在3000端口。

通過docker images可以查看目前所有的鏡像。這里可以看到每個鏡像都有一個TAG,默認值為latest,我們可以通過在鏡像名后面加:<tagname>(注意冒號)來指定TAG。

小結

至此,我們簡單嘗試了docker的鏡像構建、運行,以及遠程倉庫的同步、拉取的基本操作。

下一篇:Docker學習筆記(二):https://www.cnblogs.com/SnowPhoenix/p/14979696.html


免責聲明!

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



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