Docker入門之官方Getting-started


官方示例

官方地址:https://docs.docker.com/get-started/02_our_app/

Docker作為一個容器,可以在容器中安裝各種應用

獲取官方提供的應用示例

官方地址:https://github.com/docker/getting-started/tree/master/app

 

建立應用容器鏡像

為了創建一個應用,需要使用一個 Dockerfile

一個Dockerfile只是一個基於文本的指令腳本,用於創建容器映像。如果之前已經創建過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"]

構建docker鏡像

docker build -t getting-started .

首先會下載相應的鏡像依賴,然后開始構建,然后執行yarn指令,下載應用所需要的依賴

參數解釋 -t 和 .

  • -t 會標記我們的鏡像,可以當作起名字,這個鏡像的名字就是 getting-started,也可以理解為這個鏡像的引用
  • . 在這條構建docker指令的最后,通知Docker在當前目錄查找Dockerfile

啟動應用容器

構建步驟結束后,我們已經有了一個鏡像,通過 docker run 指令啟動容器

docker run -dp 3000:3000 getting-started

參數解釋 -d 和 -p

  • -d,Run container in background and print container ID,后台運行並打印容器的ID
  • -p,Publish a container's port(s) to the host,給主機暴露該容器的端口

當前指令的意思是把容器的3000端口映射到主機的3000端口,沒有這個映射我們不能直接管理應用

更新應用

需要查看容器列表然后停下指定容器

docker ps

使用docker stop指令

# Swap out <the-container-id> with the ID from docker ps
docker stop <the-container-id>

刪除該容器

docker rm <the-container-id>

也可以使用強制刪除指令

docker rm -f <the-container-id>

把新的應用覆蓋上去,然后重新啟動即可

docker run -dp 3000:3000 getting-started

共享應用

現在我們已經創建好了鏡像,可以共享鏡像了,必須使用一個Docker注冊處才能實現共享,默認的注冊處是Docker Hub,我們所有使用的鏡像都來源於注冊處

一個Docker ID允許訪問Docker Hub,這是世界上最大的容器鏡像的庫和社區

創建Docker ID的鏈接:https://hub.docker.com/signup

創建倉庫

在推送一個鏡像前,我們首先需要在Docker Hub上創建一個倉庫

  • 注冊並使用Docker Hub分享鏡像
  • 在Docker Hub登錄
  • 點擊創建倉庫按鈕
  • 使用getting-started給倉庫設置名字,保證可見性為 Public

推送鏡像

$ docker push docker/getting-started
The push refers to repository [docker.io/docker/getting-started]
An image does not exist locally with the tag: docker/getting-started

在命令行中,嘗試運行在Docker Hub上看到的push命令。請注意,您的命令將使用您的命名空間,而不是“docker”

為什么失敗了?push命令正在查找名為docker/getting started的圖像,但沒有找到。如果運行docker image ls,也不會看到

要解決這個問題,我們需要“標記”我們已經建立的現有鏡像,以給它起另一個名字

使用一下指令在Docker Hub上登錄

docker login -u YOUR-USER-NAME

使用docker tag命令為getting-start賦予一個新名稱,一定要把你的用戶名換成你的Docker ID

docker tag getting-started YOUR-USER-NAME/getting-started

現在再試一次你的推送命令

docker push YOUR-USER-NAME/getting-started

如果要從Docker Hub復制值,可以刪除標記名部分,因為我們沒有向圖像名稱添加標記。如果不指定標記,Docker將使用名為latest的標記

持久化數據庫

容器的文件系統

當一個容器運行時,它使用來自一個映像的不同層作為它的文件系統。每個容器也有自己的“暫存空間”來創建/更新/刪除文件。在另一個容器中看不到任何更改,即使它們使用相同的圖像

開啟一個Ubuntu容器,該容器能夠創建一個被稱為 /data.txt 的文件,該文件攜帶1到10000的隨機數

docker run -d ubuntu bash -c "shuf -i 1-10000 -n 1 -o /data.txt && tail -f /dev/null"

這條命令,啟動一個bashshell並調用兩個命令(所以使用&&)。第一部分選取一個隨機數並將其寫入/data.txt。第二個命令只是監視一個文件以保持容器運行

我們可以通過在容器中執行來查看輸出。為此,打開儀表板並單擊運行ubuntu映像的容器的第一個操作

您將看到在ubuntu容器中運行shell的終端,運行以下命令以查看/data.txt文件的內容,之后再次關閉此終端

cat /data.txt

如果喜歡命令行,也可以使用docker exec命令來執行相同的操作,首先需要獲取容器的ID(使用docker ps獲取它)並使用以下命令獲取內容

docker exec <container-id> cat /data.txt

同時啟動另一個ubuntu容器(相同的鏡像),我們將看到沒有相同的文件

docker run -it ubuntu ls /

通過以下指令刪除容器

docker rm -f

容器卷

在前面的實驗中,我們看到每個容器每次啟動時都從圖像定義開始

雖然容器可以創建、更新和刪除文件,但當容器被刪除並且所有更改都與該容器隔離時,這些更改將丟失,通過卷,我們可以改變這一切

卷提供了將容器的特定文件系統路徑連接回主機的能力,如果裝載了容器中的目錄,則在主機上也會看到該目錄中的更改,如果我們跨容器重新啟動裝載相同的目錄,我們會看到相同的文件

有兩種主要的卷類型。我們最終將兩者都使用,但我們將從命名卷開始

Persist the todo data

默認情況下,todo應用程序將其數據存儲在SQLite數據庫/etc/todos/todo.db中

由於數據庫是一個單獨的文件,如果我們能夠將該文件持久化到主機上並使其可供下一個容器使用,那么它應該能夠從上一個容器停止的地方恢復

通過創建一個卷並將其附加(通常稱為“裝載”)到存儲數據的目錄中,我們可以持久化數據,當容器寫入todo.db文件時,它將被持久化到卷中的主機

將命名卷簡單地看作一個數據桶

Docker維護磁盤上的物理位置,您只需記住卷的名稱,每次使用卷時,Docker都會確保提供正確的數據

通過下面指令創建卷

docker volume create todo-db

在儀表板中再次停止並移除todo應用程序容器(或使用docker rm-f<id>),因為它仍在運行,而不使用持久卷

啟動todo應用程序容器,添加-v標志以指定卷裝載,將使用命名卷並將其裝載到/etc/todos,它將捕獲在該路徑上創建的所有文件

docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started

一旦容器啟動,打開應用程序並將一些項目添加到您的待辦事項列表中

 

停止並刪除todo應用程序的容器,使用儀表板或 docker ps 獲取ID,然后使用 docker rm-f<ID> 將其刪除

Dive into the volume(偵測卷)

使用下面這條指令

docker volume inspect
docker volume inspect todo-db
[
    {
        "CreatedAt": "2019-09-26T02:18:36Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/todo-db/_data",
        "Name": "todo-db",
        "Options": {},
        "Scope": "local"
    }
]

掛載點是磁盤上存儲數據的實際位置,在大多數計算機上,您需要具有root訪問權限才能從主機訪問此目錄

在Docker Desktop中運行時,Docker命令實際上是在您機器上的一個小虛擬機中運行的,如果您想查看Mountpoint目錄的實際內容,則需要首先進入VM內部

每次更改重建圖像都需要相當長的時間,使用綁定掛載,這個方法更好!

綁定掛載

通過綁定掛載

  • 可以控制主機上的確切掛載點
  • 可以使用它來持久化數據,但它通常用於向容器提供額外的數據
  • 在處理應用程序時,可以使用綁定掛載將源代碼掛載到容器中,讓它看到代碼更改、響應,並立即看到更改
  • 對於基於節點的應用程序,nodemon是一個很好的工具,可以監視文件更改,然后重新啟動應用程序

在大多數其他語言和框架中都有相應的工具

快速卷類型比較

綁定裝載和命名卷是Docker引擎附帶的兩種主要類型的卷。但是,可以使用其他卷驅動程序來支持其他用例(SFTP、Ceph、NetApp、S3等)

  Named Volumes Bind Mounts
Host Location Docker chooses You control
Mount Example (using -v) my-volume:/usr/local/data /path/to/data:/usr/local/data
Populates new volume with container contents Yes No
Supports Volume Drivers Yes No

開啟一個調試模式的容器

要運行容器以支持開發工作流,我們將執行以下操作:

  • 將源代碼裝入容器
  • 安裝所有依賴項,包括“dev”依賴項
  • 啟動nodemon以監視文件系統更改

確保沒有運行任何以前的getting-start容器,運行以下命令

docker run -dp 3000:3000 \
     -w /app -v "$(pwd):/app" \
     node:12-alpine \
     sh -c "yarn install && yarn run dev"

如果用的是windows的powershell,那么使用以下指令

docker run -dp 3000:3000 `
     -w /app -v "$(pwd):/app" `
     node:12-alpine `
     sh -c "yarn install && yarn run dev"

參數解釋

  • -dp 3000:3000 - Run in detached (background) mode and create a port mapping,后台運行並創建一個端口映射
  • -w /app - sets the “working directory” or the current directory that the command will run from,設置工作目錄或者指令運行的當前目錄
  • -v "$(pwd):/app" - bind mount the current directory from the host in the container into the /app directory,綁定掛載從/app目錄下的容器下的主機中的當前目錄
  • node:12-alpine - the image to use. Note that this is the base image for our app from the Dockerfile
  • sh -c "yarn install && yarn run dev" - the command. We’re starting a shell using sh(alpine doesn’t have bash) and running yarn install to install all dependencies and then running yarn run dev. If we look in the package.json, we’ll see that the dev script is starting nodemon.

您可以使用docker logs-f<container id>查看日志

docker logs -f <container-id>
 $ nodemon src/index.js
 [nodemon] 1.19.2
 [nodemon] to restart at any time, enter `rs`
 [nodemon] watching dir(s): *.*
 [nodemon] starting `node src/index.js`
 Using sqlite database at /etc/todos/todo.db
 Listening on port 3000

停止容器並構建新鏡像

docker build -t getting-started .

對於本地開發設置,使用綁定裝載非常常見,其優點是開發人員機器不需要安裝所有的構建工具和環境

使用一個docker run命令,將拉動開發環境並准備就緒

多容器應用

我們現在想將MySQL添加到應用程序堆棧中,下面的問題經常出現

  • MySQL將在哪里運行?
  • 在同一個容器中安裝或單獨運行?

一般來說,每個容器都應該做一件事並做好它

  • 很有可能目前必須以不同於數據庫的方式擴展API和前端
  • 單獨的容器允許獨立地設置版本和更新版本
  • 雖然可以在本地為數據庫使用容器,但更希望在生產環境中為數據庫使用托管服務,沒人想把數據庫引擎和自己的應用一起發布
  • 運行多個進程將需要一個進程管理器(容器只啟動一個進程),這增加了容器啟動/關閉的復雜性

因此,我們將更新我們的應用程序以如下方式工作:

 

 

容器網絡系統

在默認情況下,容器是獨立運行的,不知道同一台計算機上的其他進程或容器的任何信息

如果兩個容器在同一個網絡上,它們可以相互通信,如果他們不是,他們就不能

啟動MySQL

將容器放到網絡上有兩種方法

  • 在開始時分配它
  • 連接現有容器

現在,我們將首先創建網絡,並在啟動時附加MySQL容器

創建網絡

docker network create todo-app

啟動一個MySQL容器並將其連接到網絡,定義一些數據庫用來初始化數據庫的環境變量

參閱MySQL Docker Hub清單中的“環境變量”部分--https://hub.docker.com/_/mysql/

docker run -d \
     --network todo-app --network-alias mysql \
     -v todo-mysql-data:/var/lib/mysql \
     -e MYSQL_ROOT_PASSWORD=secret \
     -e MYSQL_DATABASE=todos \
     mysql:5.7

如果使用windows的powershell

docker run -d `
     --network todo-app --network-alias mysql `
     -v todo-mysql-data:/var/lib/mysql `
     -e MYSQL_ROOT_PASSWORD=secret `
     -e MYSQL_DATABASE=todos `
     mysql:5.7

這里使用了一個名為todo mysql data的卷,並將其掛載在/var/lib/mysql上

mysql就是在這里存儲數數據,我們從未運行docker volume create命令,Docker認識到我們想要使用一個命名卷,並自動為我們創建一個

為了確認數據庫已經啟動並運行,連接到數據庫並校驗連接

docker exec -it <mysql-container-id> mysql -u root -p

當密碼提示出現時,鍵入密碼,在MySQL shell中,列出數據庫並驗證您看到了todos數據庫。

mysql> SHOW DATABASES;

輸出可能是這個樣子

 +--------------------+
 | Database           |
 +--------------------+
 | information_schema |
 | mysql              |
 | performance_schema |
 | sys                |
 | todos              |
 +--------------------+
 5 rows in set (0.00 sec)

連接到MySQL

現在我們知道MySQL已經啟動並運行了,如果在同一網絡上運行另一個容器,如何找到該容器(記住每個容器都有自己的IP地址)?

為了解決這個問題,我們將使用nicolaka/netshot容器,它附帶了很多工具,這些工具對於解決或調試網絡問題非常有用。

使用nicolaka/netshoot映像啟動新容器,確保將其連接到同一網絡

docker run -it --network todo-app nicolaka/netshoot

在容器內部,我們將使用dig命令,這是一個有用的DNS工具,我們將查找主機名mysql的IP地址

dig mysql

輸出應該是

; <<>> DiG 9.14.1 <<>> mysql
 ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 32162 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;mysql. IN A ;; ANSWER SECTION: mysql. 600 IN A 172.23.0.2 ;; Query time: 0 msec ;; SERVER: 127.0.0.11#53(127.0.0.11) ;; WHEN: Tue Oct 01 23:47:24 UTC 2019 ;; MSG SIZE rcvd: 44

在“ANSWER”部分,您將看到mysql的A記錄,解析為172.23.0.2(您的IP地址很可能有不同的值)

雖然mysql通常不是有效的主機名,但Docker能夠將其解析為具有該網絡別名的容器的IP地址(還記得我們前面使用的--network alias標志嗎?)

這意味着應用程序只需要連接到一個名為mysql的主機,它就會與數據庫通信

使用MySQL運行APP

todo應用程序支持設置一些環境變量來指定MySQL連接設置

  • MYSQL_HOST - the hostname for the running MySQL server
  • MYSQL_USER - the username to use for the connection
  • MYSQL_PASSWORD - the password to use for the connection
  • MYSQL_DB - the database to use once connected

雖然使用env vars來設置連接設置對於開發來說通常是可以的,但是在生產環境中運行應用程序時非常不鼓勵這樣做

更安全的機制是使用容器編排框架提供的秘密支持。在大多數情況下,這些秘密作為文件裝載在正在運行的容器中

您將看到許多應用程序(包括MySQL映像和todo應用程序)也支持env vars,並帶有一個∗FILE后綴來指向包含該變量的文件

例如,設置MYSQL\u PASSWORD\u FILE var將導致應用程序使用引用文件的內容作為連接密碼

Docker沒有做任何事情來支持這些環境變量,您的應用程序需要知道如何查找變量並獲取文件內容

我們將指定上面的每個環境變量,並將容器連接到我們的應用程序網絡

docker run -dp 3000:3000 \
   -w /app -v "$(pwd):/app" \ --network todo-app \ -e MYSQL_HOST=mysql \ -e MYSQL_USER=root \ -e MYSQL_PASSWORD=secret \ -e MYSQL_DB=todos \ node:12-alpine \ sh -c "yarn install && yarn run dev"

如果使用windows的powershell

docker run -dp 3000:3000 `
   -w /app -v "$(pwd):/app" ` --network todo-app ` -e MYSQL_HOST=mysql ` -e MYSQL_USER=root ` -e MYSQL_PASSWORD=secret ` -e MYSQL_DB=todos ` node:12-alpine ` sh -c "yarn install && yarn run dev"

如果我們查看容器的日志(docker logs<container id>),我們應該會看到一條消息,指示它正在使用mysql數據庫。

# Previous log messages omitted
 $ nodemon src/index.js [nodemon] 1.19.2 [nodemon] to restart at any time, enter `rs` [nodemon] watching dir(s): *.* [nodemon] starting `node src/index.js` Connected to mysql db at host mysql Listening on port 3000

連接mysql

docker exec -it <mysql-container-id> mysql -p todos

輸出

mysql> select * from todo_items;
 +--------------------------------------+--------------------+-----------+
 | id                                   | name               | completed |
 +--------------------------------------+--------------------+-----------+
 | c906ff08-60e6-44e6-8f49-ed56a0853e85 | Do amazing things! |         0 |
 | 2912a79e-8486-4bc3-a4c5-460793a575ab | Be awesome!        |         0 |
 +--------------------------------------+--------------------+-----------+

通過docker-compose將前面的操作合並,使得項目管理更加人性化

使用Docker Compose

Docker Compose是一個用於幫助定義和共享多容器應用程序的工具

使用Compose,我們可以創建一個YAML文件來定義服務,並且通過一個命令,可以將所有內容都擰在一起起來或扯開

使用Compose的最大優點是,您可以在文件中定義應用程序堆棧,將其保留在項目repo的根目錄下(它現在是版本控制的),並且可以方便地讓其他人為您的項目貢獻力量

安裝Docker Compose

如果您為Windows或Mac安裝了Docker Desktop/Toolbox,那么您已經擁有Docker Compose!

如果您在Linux機器上,則需要安裝Docker Compose:https://docs.docker.com/compose/install/

安裝結束后查看docker compose的版本

docker-compose version

創建一個compose文件

在應用程序項目的根目錄下,創建一個名為docker-compose.yml的文件

在compose文件中,我們將首先定義模式版本,在大多數情況下,最好使用支持的最新版本

version: "3.7"

接下來定義要作為應用程序的一部分運行的服務(或容器)列表

version: "3.7"

services:

現在可以一次性把一個服務遷移到一個compose文件中

定義應用服務

這個是之前用來定義應用容器的指令

docker run -dp 3000:3000 \
  -w /app -v "$(pwd):/app" \ --network todo-app \ -e MYSQL_HOST=mysql \ -e MYSQL_USER=root \ -e MYSQL_PASSWORD=secret \ -e MYSQL_DB=todos \ node:12-alpine \ sh -c "yarn install && yarn run dev"

如果使用windows的power shell,使用下面指令

docker run -dp 3000:3000 `
  -w /app -v "$(pwd):/app" ` --network todo-app ` -e MYSQL_HOST=mysql ` -e MYSQL_USER=root ` -e MYSQL_PASSWORD=secret ` -e MYSQL_DB=todos ` node:12-alpine ` sh -c "yarn install && yarn run dev"

首先,為容器定義服務入口和鏡像。我們可以為這項服務取任何名字。該名稱將自動成為網絡別名,這在定義MySQL服務時非常有用

 version: "3.7"

 services:
   app:
     image: node:12-alpine

通常會在鏡像定義部分看到命令,盡管對排序沒有要求

 version: "3.7"

 services:
   app:
     image: node:12-alpine command: sh -c "yarn install && yarn run dev"

通過定義服務的ports來遷移命令的 -p3000:3000 部分

 version: "3.7"

 services:
   app:
     image: node:12-alpine command: sh -c "yarn install && yarn run dev" ports: - 3000:3000

接下來,我們將使用工作目錄和卷定義來遷移工作目錄(-w/app)和卷映射(-v“$(pwd):/app”)

Docker Compose卷定義的一個優點是我們可以使用當前目錄中的相對路徑

 version: "3.7"

 services:
   app:
     image: node:12-alpine command: sh -c "yarn install && yarn run dev" ports: - 3000:3000 working_dir: /app volumes: - ./:/app

最后,使用environment鍵遷移環境變量定義

version: "3.7"

 services:
   app:
     image: node:12-alpine command: sh -c "yarn install && yarn run dev" ports: - 3000:3000 working_dir: /app volumes: - ./:/app environment: MYSQL_HOST: mysql MYSQL_USER: root MYSQL_PASSWORD: secret MYSQL_DB: todos

定義mysql服務

之前用過的容器定義指令

docker run -d \
  --network todo-app --network-alias mysql \ -v todo-mysql-data:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORD=secret \ -e MYSQL_DATABASE=todos \ mysql:5.7

如果用windows的power shell則使用下面指令

docker run -d `
  --network todo-app --network-alias mysql ` -v todo-mysql-data:/var/lib/mysql ` -e MYSQL_ROOT_PASSWORD=secret ` -e MYSQL_DATABASE=todos ` mysql:5.7

我們將首先定義新的服務並將其命名為mysql,以便它自動獲取網絡別名。我們將繼續並指定要使用的鏡像

version: "3.7"

 services:
   app:
     # The app service definition
   mysql:
     image: mysql:5.7

接下來,我們將定義卷映射。當我們使用docker run運行容器時,命名卷是自動創建的

但是,使用Compose運行時不會發生這種情況,需要在頂層volumes:部分中定義卷,然后在服務配置中指定掛載點。只需提供卷名,就可以使用默認選項。不過,還有更多的選擇

version: "3.7"

 services:
   app:
     # The app service definition
   mysql:
     image: mysql:5.7 volumes: - todo-mysql-data:/var/lib/mysql volumes: todo-mysql-data:

最后我們只需要描述環境變量

version: "3.7"

 services:
   app:
     # The app service definition
   mysql:
     image: mysql:5.7 volumes: - todo-mysql-data:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: secret MYSQL_DATABASE: todos volumes: todo-mysql-data:

此時完整的 docker-compose.yml 文件內容應該如下所示

version: "3.7"

services:
  app:
    image: node:12-alpine command: sh -c "yarn install && yarn run dev" ports: - 3000:3000 working_dir: /app volumes: - ./:/app environment: MYSQL_HOST: mysql MYSQL_USER: root MYSQL_PASSWORD: secret MYSQL_DB: todos mysql: image: mysql:5.7 volumes: - todo-mysql-data:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: secret MYSQL_DATABASE: todos volumes: todo-mysql-data:

運行應用棧

保證沒有其他的應用/數據庫備份在之前運行

docker ps and docker rm -f <ids>

使用下面指令啟動應用棧,我們需要使用 -d 標識位使得一切都在后台運行

docker-compose up -d

理論上終端的輸出為

Creating network "app_default" with the default driver
Creating volume "app_todo-mysql-data" with default driver Creating app_app_1 ... done Creating app_mysql_1 ... done

卷和一個網絡一起創建的,默認情況下,Docker Compose會自動為應用程序棧創建一個專門的網絡,這就是為什么沒有在Compose文件中定義一個網絡

使用下面命令查看日志,將看到每個服務的日志交織到一個流中

docker compose logs-f

f標志“跟隨”日志,因此將在生成時提供實時輸出

mysql_1  | 2019-10-03T03:07:16.083639Z 0 [Note] mysqld: ready for connections.
 mysql_1  | Version: '5.7.27'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306 MySQL Community Server (GPL) app_1 | Connected to mysql db at host mysql app_1 | Listening on port 3000

服務名稱顯示在行的開頭(通常是彩色的),以幫助區分消息。如果要查看特定服務的日志,可以將服務名稱添加到logs命令的末尾,例如

docker-compose logs -f app

當應用程序啟動時,它實際上會在那里等待MySQL啟動並准備就緒,然后再嘗試連接到它

Docker沒有任何內置的支持來等待另一個容器完全啟動、運行並准備就緒,然后再啟動另一個容器,對於基於節點的項目,可以使用等待端口依賴關系

把服務全部停掉

當准備把服務全部停掉時,只需要運行指令

docker-compose down

所有容器都會停止而且網絡會被移除

刪除卷

默認情況下,運行docker compose時不會刪除compose文件中的命名卷。如果要刪除卷,則需要添加--volumes標志

docker-compose down --volumes

鏡像構建最佳實踐

構建映像后,最好使用docker scan命令對其進行安全漏洞掃描(Docker與Snyk合作提供漏洞掃描服務)

docker scan getting-started

掃描使用一個不斷更新的漏洞的數據庫,因此看到的輸出將隨着新漏洞的發現而變化,但可能如下所示:

✗ Low severity vulnerability found in freetype/freetype
  Description: CVE-2020-15999 Info: https://snyk.io/vuln/SNYK-ALPINE310-FREETYPE-1019641 Introduced through: freetype/freetype@2.10.0-r0, gd/libgd@2.2.5-r2 From: freetype/freetype@2.10.0-r0 From: gd/libgd@2.2.5-r2 > freetype/freetype@2.10.0-r0 Fixed in: 2.10.0-r1 ✗ Medium severity vulnerability found in libxml2/libxml2 Description: Out-of-bounds Read Info: https://snyk.io/vuln/SNYK-ALPINE310-LIBXML2-674791 Introduced through: libxml2/libxml2@2.9.9-r3, libxslt/libxslt@1.1.33-r3, nginx-module-xslt/nginx-module-xslt@1.17.9-r1 From: libxml2/libxml2@2.9.9-r3 From: libxslt/libxslt@1.1.33-r3 > libxml2/libxml2@2.9.9-r3 From: nginx-module-xslt/nginx-module-xslt@1.17.9-r1 > libxml2/libxml2@2.9.9-r3 Fixed in: 2.9.9-r4

輸出列出了漏洞的類型、要了解更多信息的URL,以及修復漏洞的相關庫的版本

除了在命令行上掃描新構建的鏡像外,還可以配置Docker Hub自動掃描所有新推的鏡像

鏡像分層

使用docker image history命令,可以看到用於在圖像中創建每個層的命令

docker image history getting-started

你可以看到以下輸出

IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
 a78a40cbf866        18 seconds ago      /bin/sh -c #(nop)  CMD ["node" "src/index.j…    0B                  
 f1d1808565d6        19 seconds ago      /bin/sh -c yarn install --production            85.4MB a2c054d14948 36 seconds ago /bin/sh -c #(nop) COPY dir:5dc710ad87c789593… 198kB 9577ae713121 37 seconds ago /bin/sh -c #(nop) WORKDIR /app 0B b95baba1cfdb 13 days ago /bin/sh -c #(nop) CMD ["node"] 0B <missing> 13 days ago /bin/sh -c #(nop) ENTRYPOINT ["docker-entry… 0B <missing> 13 days ago /bin/sh -c #(nop) COPY file:238737301d473041… 116B <missing> 13 days ago /bin/sh -c apk add --no-cache --virtual .bui… 5.35MB <missing> 13 days ago /bin/sh -c #(nop) ENV YARN_VERSION=1.21.1 0B <missing> 13 days ago /bin/sh -c addgroup -g 1000 node && addu… 74.3MB <missing> 13 days ago /bin/sh -c #(nop) ENV NODE_VERSION=12.14.1 0B <missing> 13 days ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B <missing> 13 days ago /bin/sh -c #(nop) ADD file:e69d441d729412d24… 5.59MB 

每一行代表圖像中的一層。這里顯示的是底部的底座和頂部最新的一層,使用此功能,還可以快速查看每個層的大小,幫助診斷大型圖像

您會注意到有幾行被截斷了,如果添加--no trunc標志,您將得到完整的輸出

docker image history --no-trunc getting-started

層緩存

已經看到了分層的實際效果,那么有一個重要的前車之鑒可以幫助減少容器映像的構建時間

一旦圖層發生變化,所有下游圖層也必須重新創建

再看一次Dockerfile

# syntax=docker/dockerfile:1
FROM node:12-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]

回到圖像歷史輸出,我們看到Dockerfile中的每個命令都成為圖像中的一個新層。

您可能還記得,當我們對映像進行更改時,必須重新安裝依賴項,每次構建時都圍繞相同的yarn依賴項進行發布是沒有多大意義

要解決這個問題,我們需要重新構造Dockerfile,以幫助支持依賴項的緩存

對於基於節點的應用程序,這些依賴項在package.json文件中定義。所以,如果我們先只復制那個文件,安裝依賴項,然后再復制其他所有內容

最后我們只在package.json發生更改時重新創建yarn依賴關系即可

首先更新Dockerfile以在package.json中復制,安裝依賴項,然后復制包中的所有其他內容。 

# syntax=docker/dockerfile:1
 FROM node:12-alpine
 WORKDIR /app
 COPY package.json yarn.lock ./
 RUN yarn install --production
 COPY . .
 CMD ["node", "src/index.js"]

在Dockerfile所在的文件夾中創建一個名為.dockerginore的文件,該文件包含以下內容

node_modules

.dockerginore文件是一種僅選擇性地復制圖像相關文件的簡單方法,在這種情況下,在第二個復制步驟中應該省略node\u modules文件夾

因為否則,它可能會覆蓋由RUN步驟中的命令創建的文件,有關為什么建議對Node.js應用程序和其他最佳實踐使用此方法的更多詳細信息,請參閱他們的Node.js web應用程序停靠指南

使用docker build指令創建一個新鏡像

docker build -t getting-started .

輸出如下

Sending build context to Docker daemon  219.1kB
 Step 1/6 : FROM node:12-alpine
 ---> b0dc3a5e5e9e
 Step 2/6 : WORKDIR /app
 ---> Using cache
 ---> 9577ae713121
 Step 3/6 : COPY package.json yarn.lock ./
 ---> bd5306f49fc8
 Step 4/6 : RUN yarn install --production
 ---> Running in d53a06c9e4c2
 yarn install v1.17.3
 [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 10.89s.
 Removing intermediate container d53a06c9e4c2
 ---> 4e68fbc2d704
 Step 5/6 : COPY . .
 ---> a239a11f68d8
 Step 6/6 : CMD ["node", "src/index.js"]
 ---> Running in 49999f68df8f
 Removing intermediate container 49999f68df8f
 ---> e709c03bc597
 Successfully built e709c03bc597
 Successfully tagged getting-started:latest

現在在前端html文件隨意更改一處,重新執行一下指令

docker build -t getting-started .

現在輸出如下

 Sending build context to Docker daemon  219.1kB
 Step 1/6 : FROM node:12-alpine
 ---> b0dc3a5e5e9e
 Step 2/6 : WORKDIR /app
 ---> Using cache
 ---> 9577ae713121
 Step 3/6 : COPY package.json yarn.lock ./
 ---> Using cache
 ---> bd5306f49fc8
 Step 4/6 : RUN yarn install --production
 ---> Using cache
 ---> 4e68fbc2d704
 Step 5/6 : COPY . .
 ---> cccde25a3d9a
 Step 6/6 : CMD ["node", "src/index.js"]
 ---> Running in 2be75662c150
 Removing intermediate container 2be75662c150
 ---> 458e5c6f080c
 Successfully built 458e5c6f080c
 Successfully tagged getting-started:latest

注意看1到4步,using cache,已經使用了緩存

多階段構建優點

  • 將生成時依賴項與運行時依賴項分開
  • 通過只提供應用程序運行所需的內容來減小整體圖像大小

Maven/Tomcat實例

在構建基於Java的應用程序時,需要使用JDK將源代碼編譯成Java字節碼

然而,JDK在生產中是不需要的,此外,可能正在使用Maven或Gradle等工具來幫助構建應用程序,在我們的最終鏡像中也不需要這些

# syntax=docker/dockerfile:1
FROM maven AS build
WORKDIR /app
COPY . .
RUN mvn package

FROM tomcat
COPY --from=build /app/target/file.war /usr/local/tomcat/webapps 
  • 在本例中,我們使用一個階段(稱為build)來使用Maven執行實際的Java構建
  • 在第二階段(從tomcat開始),我們從構建階段復制文件
  • 最后一個映像只是創建的最后一個階段(可以使用--target標志覆蓋)

React 實例

在構建React應用程序時,我們需要一個節點環境來將JS代碼(通常是JSX)、SASS樣式表等編譯成靜態HTML、JS和CSS

如果我們不做服務器端渲染,我們甚至不需要為我們的產品構建節點環境。為什么不把靜態資源放在一個靜態nginx容器中呢

# syntax=docker/dockerfile:1
FROM node:12 AS build
WORKDIR /app
COPY package* yarn.lock ./
RUN yarn install
COPY public ./public
COPY src ./src
RUN yarn run build

FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html

 


免責聲明!

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



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