docker 讓應用程式佈署在軟體容器下的工作可以自動化進行,藉此在Linux作業系統上,提供一個額外的軟體抽象層,以及作業系統層虛擬化的自動管理機制。
Docker 的優點
秒級實作
對系統資源的使用率很高,一台主機可以同時執行數千個 Docker 容器
更快速的交付和部署
使用一個標準的映像檔來建立一套開發容器,開發完成之後,維運人員可以直接使用這個容器來部署程式碼。
更有效率的虛擬化
更輕鬆的遷移和擴展
更簡單的管理
Docker vs VM
Docker 僅載入所需要的函式庫與執行檔,而不像VM需要安裝大容量的作業系統
VM是用硬體端的hypervisor技術來同時執行多個虛擬主機,一般可預見需要大量的運行資源。Docker則是用軟體端的作業系統來實現虛擬分割的技術,由於僅載入核心的函式庫
Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 查看版本
docker -- version
docker - compose -- version
docker - machine -- version
# 從 hub 取得所需要的 images :後面為版本號,若沒加則會拉最新的
docker pull ubuntu : 12 . 04
# 搜尋 Image
docker search
docker search ubuntu - f is - official = true # is-official=true 官方的意思
# container (有些 container 可改 ps)
docker container ls # List all running containers
docker container ls - a # List all vcontainers, even those not running
docker container stop < containerID > # Gracefully stop the specified container
docker container kill < containerID > # Force shutdown of the specified container
docker container rm < containerID > # Remove specified container from this machine
docker container rm $ ( docker container ls - a - q ) # Remove all containers; -q show all ID
docker start < containerID >
docker stop < containerID >
# images
docker image ls - a # List all images on this machine
docker image rm < image id > # Remove specified image from this machine
docker image rm $ ( docker image ls - a - q ) # Remove all images from this machine
docker rmi < imageID >|< imageName >
docker rmi nginx
docker rmi nginx - f
# 顯示 container 的資訊
docker inspect < containerID >
# 跳回 terminal 而不關閉 container
( ctrl + p ) + ( ctrl + q )
# 回去 container
docker attach < containerID >
# 查看 docker container 的 IP
進去 container 執行 cat /etc/ hosts
# 複製檔案到外面
docker cp < containerId > : < containerPath > < localPath >
# 複製檔案到 container 裡面
docker cp < localPath > < containerId > : < containerPath >
Run
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 執行 docker ,若本地沒有 image,就會自動去 Docker Hub pull 下來,如果沒有指定 TAG,預設使用 latest
docker run - t - i ubuntu : 12 . 04 bash
# -t 在容器中分配一個虛擬終端(pseudo-tty)並綁定到容器的標準輸入上
# -i 建立與容器標準輸入(STDIN)的互動連結,讓容器的標準輸入保持打開
# 執行後啟動 bash
docker run - d - p 80 : 80 -- name webserver nginx
# -p 80:80 (主機 port, container port)
# --name webserver
# -d daemon 模式,放到背景去跑
docker run - it - v ~ /Downloads: ubuntu:12.04 bash
# 建立容器並啟動,且掛載本地目錄(local 在前,container 在後)
# 加上 -rm 可以在 container 結束後自動刪除
docker run -it --rm ubuntu:latest bash
# 如果 container 停下來會自動 restart
--restart=always
# net 設定網路範圍
docker run --net=bridge -it ubuntu
none: 在執行 container 時,網路功能是關閉的,所以無法與此 container 連線
container: 使用相同的 Network Namespace,所以 container1 的 IP 是 172.17.0.2 那 container2 的 IP 也會是 172.17.0.2
host: container 的網路設定和實體主機使用相同的網路設定,所以 container 裡面也就可以修改實體機器的網路設定,因此使用此模式需要考慮網路安全性上的問題
bridge: Docker 預設就是使用此網路模式,這種網路模式就像是 NAT 的網路模式,例如實體主機的 IP 是 192.168.1.10 它會對應到 Container 裡面的 172.17.0.2,在啟動 Docker 的 service 時會有一個 docker0 的網路卡就是在做此網路的橋接。
overlay: container 之間可以在不同的實體機器上做連線,例如 Host1 有一個 container1,然後 Host2 有一個 container2,container1 就可以使用 overlay 的網路模式和 container2 做網路的連線。
Attach
1
2
3
# 連到 container 最後執行
# 當多個窗口同時 attach 到同一個容器的時候,所有窗口都會同步顯示。當某個窗口因命令阻塞時,其他窗口也無法執行操作了
docker attach < imageID >|< imageName >
Exec
1
2
# 進去 container 執行 bash
docker exec - it < imageID >|< imageName > bash
Volume
1
2
# Volume mapping to map the local directory /opt/datadir to /var/lib/mysql
docker run - v /opt/ datadir : /var/ lib / mysql mysql
log
1
2
# 看 container log,-f=follow, -t=time
docker logs - tf < imageID >|< imageName >
export
1
2
3
docker export nginx > nginx . tar
cat nginx . tar | docker import - nginxbak
# nginxbak 是 Import 進 Docker 取得 Image Name
stats
查看 Docker Container 的資源使用狀態
1
docker stats < container name > | < container id >
建立映像檔
1.修改已有映像檔
進去現有的 container,加入新的套件,再用此 container build 新的 images
1
2
3
4
5
6
7
# commit 建立新的 images
# docker commit -m '<message>' -a '<author>' <containerID> <tag>
docker commit - m "Added json gem" - a "Docker Newbee" 0 b2616b0e5a8 ouruser / sinatra : v2
# -m 指定提交的說明信息,跟我們使用的版本控制工具一樣;
# -a 可以指定更新的使用者信息
# 之後是用來建立映像檔的容器的 CONTAINER ID
# 最後指定新映像檔的名稱和 tag 。建立成功後會印出新映像檔的 ID。
Tag and Publish the image
要 push 的話,必須將 image tag 成 username/imagename 的形式才可以 push
1
2
3
4
5
6
# Tag the image
docker tag username / repository : tag
docker tag helloworld leon / get - started : part1
# Upload your tagged image to the repository:
docker push leon / get - started : part1
Pull and run the image from the remote repository
1
docker run - p 4000 : 80 username / repository : tag
儲存映像檔
如果要建立映像檔到本地檔案,可以使用 docker save 命令
1
2
3
4
5
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
ubuntu 14 . 04 c4ff7513909d 5 weeks ago 225 . 4 MB
docker save - o ubuntu_14 . 04 . tar ubuntu : 14 . 04
載入映像檔
可以使用 docker load 從建立的本地檔案中再匯入到本地映像檔庫
1
2
docker load -- input ubuntu_14 . 04 . tar
docker load < ubuntu_14 . 04 . tar
attach 開啟一個和正在運行的處理序交互的終端,如果該處理序結束,原docker container的處理序也會結束。attach只可以用在以 /bin/bash 命令啟動的容器, 比如 docker run ubuntu /bin/bash
attach 命令有時候並不方便。當多個窗口同時 attach 到同一個容器的時候,所有窗口都會同步顯示。當某個窗口因命令阻塞時,其他窗口也無法執行操作了
exec 可以開啟多個終端實例, exec -i /bin/bash,由此可見exec其實是在運行中的容器中執行一個命令,比如/bin/bash 來達到交互的目的。
Dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1. OS-Ubuntu
FROM Ubuntu
# 2. Update apt repo
RUN apt - get update
# 3. Install denpendencies using apt
RUN apt - get install python
# 4. Install python denpendencies using pip
RUN pip install flask
RUN pip install flask - mysql
# 5. Copy source code to /opt folder
COPY . /opt/sou rce - code
# 6. Run the web server using 'flask' command
ENTRYPOINT FLASK_APP = /opt/sou rce - code / app . py flask run
1
2
3
# 可加 --no-cache,避免在 Build Docker image 時被 cache 住,造成沒有 build 到修改過的 Dockerfile
docker build - t user_name / custom_app Dockerfile
docker push user_name / custom_app
Dockerfile 指令
注意一個映像檔不能超過 127 層
1
2
3
4
5
#一般會建議放置 Dockerfile 的目錄為空目錄。也可以透過 .dockerignore 檔案(每一行新增一條排除模式:exclusion patterns)來讓 Docker 忽略路徑下的目錄和檔案。
docker build - t < repositoryName > : < tagName > .
#這樣也可以,只是不指定名稱
docker build .
FROM
一定要有,只能有一個 FROM,基於某個已存在的 image 進行二次開發。
MAINTAINER
指定維護者訊息。
1
MAINTAINER < name | email >
RUN
對映像檔執行相對應的命令。每運行一條 RUN 指令,映像檔就會新增一層,主要用來安裝 packages、設定系統環境等
1
2
3
4
# shell形式
RUN < command > < param1 > < param2 >
# exec形式
RUN [ "executable" , "param1" , "param2" ]
前者將在 shell 終端中運行命令,即 /bin/sh -c
;後者則使用 exec 執行。指定使用其它終端可以透過第二種方式實作,例如 RUN ["/bin/bash", "-c", "echo hello"]
。
每條 RUN 指令將在當前映像檔基底上執行指定命令,並產生新的映像檔。當命令較長時可以使用 \ 來換行。
EXPOSE
設定 Docker 伺服器容器對外的埠號,供外界使用。在啟動容器時需要透過 -P,Docker 會自動分配一個埠號轉發到指定的埠號(當然也可以自己給,例如 -p 1055:80)
ADD
該命令將複製指定的 <src>
到容器中的 <dest>
。 其中 <src>
可以是 Dockerfile 所在目錄的相對路徑;也可以是一個 URL;還可以是一個 tar 檔案(其複製後會自動解壓縮)。
1
2
ADD < src > < dest >
ADD text . conf /etc/ apache2 / test . conf
USER
切換使用者身份,Docker 預設使用者是 root,但若不需要,建議切換使用者身份,畢竟 root 權限太大了,使用上有安全的風險。
VOLUME
建立一個可以從本地端或其他容器掛載的掛載點,一般用來存放資料庫和需要保存的資料等。
1
2
VOLUME [ "/data" ]
VOLUME [ "/storage1" , "/storage2" , "/storage2" ]
WORKDIR
用來切換工作目錄,Docker 預設工作目錄是在根目錄,只有 RUN 能執行 cd 指令切換目錄,只作用在當下的 RUN,也就是說每一個 RUN 都是獨立進行的。如果想讓其他指令在指定的目錄下執行,就得靠 WORKDIR。WORKDIR 的變更影響是持續的,不用每個指令前都使用一次 WORKDIR
1
2
3
4
5
6
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
#則最終路徑為 / a / b / c
ENV
指定一個環境變數,會被後續 RUN 指令使用,並在容器運行時保持。
1
2
3
4
5
6
ENV < key > < value >
ENV PG_MAJOR 9 . 3
ENV PG_VERSION 9 . 3 . 4
RUN curl - SL http : //ex ample . com / postgres - $PG_VERSION . tar . xz | tar - xJC /usr/s rc / postgress && …
ENV PATH /usr/ local / postgres - $PG_MAJOR / bin : $PATH
ARG
建構參數和 ENV 的效果一樣,都是設定環境變數。所不同的是,ARG 所設定的建構環境的環境變數,在將來容器運行時是不會存在這些環境變數的。但是不要因此就使用 ARG 保存密碼之類別的訊息,因為 docker history 還是可以看到所有值的。
Dockerfile 中的 ARG 指令是定義參數名稱,以及定義其預設值。
該預設值可以在建構命令
1
docker build 中用 -- build - arg < 參數名 >=< 值 >
來覆蓋。
COPY
能將本機端的檔案或目錄,複製到 image 內。(與 ADD 雷同,差別在於不會做解壓縮,一般都是使用COPY)
1
2
3
#複製本地端的 <src>(為 Dockerfile 所在目錄的相對路徑)到容器中的 <dest>。
COPY < src > < dest >
COPY test . rb . /
ENTRYPOINT
指定 Docker image 運行成 instance (也就是 Docker container) 時,要執行的指令或檔案。在這個範本中,test.rb 就是要執行的檔案。
1
2
3
4
5
6
ENTRYPOINT [ "./test.rb" ]
# shell形式
ENTRYPOINT [ "executable" , "param1" , "param2" ]
# exec形式
ENTRYPOINT command param1 param2
會將 docker run xxx 後面的參數,當作 ENTRYPOINT 指令的參數
1
2
3
ENTRYPOINT [ "echo" ]
docker run CONTAINER echo foo
# => echo foo
CMD
指定啟動容器時執行的命令,每個 Dockerfile 只能有一條 CMD 命令。如果指定了多條命令,只有最後一條會被執行。
1
2
3
4
5
6
7
8
#在 /bin/sh 中執行,使用在給需要互動的指令
# shell形式
CMD command param1 param2
# exec形式
CMD [ "executable" , "param1" , "param2" ]
#參數形式: 作為 ENTRYPOINT 的預設參數
CMD [ "param1" , "param2" ]
會將 docker run xxx 後面的參數,覆蓋掉 CMD 指定的命令。
1
2
3
CMD [ "echo" ]
docker run CONTAINER echo foo
# => foo
RUN ENTRYPOINT & CMD
shell & exec 形式差別
shell
預設使用 /bin/sh -c
來運行命令,如果鏡像中不包含/bin/sh,容器會無法啟動。
exec (推薦的格式)
exec 直接運行指定的指令
exec指定的命令不由shell啟動,因此也就無法使用shell中的環境變數,如$HOME
。需要環境變數可以指定命令為 sh
:CMD [ "sh", "-c", "echo", "$HOME" ]
範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 記得要指定版本,不然很容易因為版本導致這個 Dockerfile 無法使用
FROM ubuntu : 14 . 04
RUN apt - get update - qq && apt - get install - y aptitude git imagemagick curl vim bash - completion htop nodejs mysql - client libmysqlclient - dev
RUN gpg -- keyserver hkp : // keys . gnupg . net -- recv - keys 409 B6B1796C275462A1703113804BB82D39DC0E3
RUN \ curl - sSL https : // get . rvm . io | bash - s stable
RUN /bin/ bash - l - c "rvm install 2.1.5"
RUN /bin/ bash - l - c "gem install bundler -v 1.10.5 --no-ri --no-rdoc"
ENV APP_ROOT /project_name
RUN mkdir $APP_ROOT
WORKDIR $APP_ROOT
ADD . $APP_ROOT
RUN / bin / bash - l - c "bundle install"
範例二
slack_neuralyzer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
FROM ubuntu : 14 . 04
MAINTAINER leonji mgleon08 @gmail . com
RUN apt - get update
RUN apt - get - y upgrade
RUN apt - get - y install curl
RUN gpg -- keyserver hkp : // keys . gnupg . net -- recv - keys 409 B6B1796C275462A1703113804BB82D39DC0E3 7 D2BAF1CF37B13E2069D6956105BD0E739499BDB
RUN curl - sSL https : // get . rvm . io | bash - s stable
RUN /bin/ bash - l - c "rvm install 2.4.2"
RUN /bin/ bash - l - c "gem install slack_neuralyzer"
CMD /bin/ bash - l - c "slack_neuralyzer -t <SLACK_TOKEN> -u <USER_NAME> -D <USER_NAME> -m -e -r 1"
1
2
3
4
5
6
7
8
# 可加上 --no-cache,避免在 Build Docker image 時被 cache 住,而造成沒有 build 到修改過的 Dockerfile。
docker build - t leon : slack
# 放到背景去跑,跑完就結束
docker run - d < imageID >|< imageName >
# 之後會產生 container
docker ps - a
docker start < containerID >
# 就會跑了,而且會連同原本 run 加上的 -d 參數也會一起執行
bin/bash -l,中的 -l 6.1 Invoking Bash
.env file
docker alias
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# stop all running containers
alias dstopall = 'docker stop $(docker ps --no-trunc -aq)'
# remove all containers
alias drmall = 'docker rm $(docker ps -a -q)'
# remove temp image
alias drminone = 'docker rmi $(docker images -f dangling=true -q)'
# or use function
function dstopall () {
docker stop `docker ps --no-trunc -aq`
}
# remove all containers
function drmall () {
docker rm `docker ps -a -q`
}
# remove temp image
function drminone () {
docker rmi `docker images -f dangling=true -q`
}
官方文件:
參考文件: