docker 在 mac 上原本是用 boot2docker,但現在已經有了原生的 Docker for mac,Docker for mac 不再是使用 VirtualBox,而是改用比較輕量化的 macOS 虛擬機 HyperKit。
Docker for mac 需要 OS X El Capitan 10.11 以上的作業系統,以及 2010 或更新 Mac 硬體,以支援 MMU virtualization。
在 Get started with Docker for Mac 這個網頁可以下載 docker,目前分為 Stable 及 Beta 兩個版本,我們是安裝 Stable 版本,但還是隨時可以切換到 Beta 版。
安裝完成並執行後,可以在狀態列看到 docker 那隻鯨魚。
docker 的元件
- 客戶端及伺服器: client 發送 request 給 docker daemon,由 damone 代理工作並回傳結果
image: 由指令一步一步建構出來的 image,類似 container 的 source code
registry: docker 以 registry 保存 image,分為 public 及 private 兩種,公用的 registry 稱為 docker hub,可以自己架設 private registry。
container: image 是 docker 的建構及打包對象,而 container 是啟動跟執行的目標。
docker 的技術元件
- libcontainer: 就是原生的 Linux 容器
- 獨立的 kernel namespace 用來隔離文件、process、網路
- 每個 container 都有自己的 /root,獨立的文件系統
- 獨立的 process
- 獨立的網路
- 資源隔離及分組: 使用 cgroups 將 CPU 及記憶體獨立分配給每個 container
copy-on-write: 分層的文件系統,但 user 還是只能看到所有文件疊加後的結果,會覺得只有一個文件系統
log: container 的 STDIN, STDOUT, STDERR 都會記錄到 log中
互動式 shell: 可建立虛擬的 tty terminal,連接到 STDIN
測試 docker
我們可以利用 ubuntu 的 image,執行一個 echo 指令進行 docker 的測試,在安裝了 docker for mac 之後,就可以直接在 terminal 使用 docker 的 command line tool。
$ docker run -it ubuntu echo "Hello World"
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
b3e1c725a85f: Pull complete
4daad8bdde31: Pull complete
63fe8c0068a8: Pull complete
4a70713c436f: Pull complete
bd842a2105a8: Pull complete
Digest: sha256:7a64bc9c8843b0a8c8b8a7e4715b7615e4e1b0d8ca3c7e7a76ec8250899c397a
Status: Downloaded newer image for ubuntu:latest
Hello World
我們也可以直接執行一個 nginx web server
$ docker run -d -p 80:80 --name webserver nginx
nable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
75a822cd7888: Already exists
64f0219ba3ea: Pull complete
325b624bee1c: Pull complete
Digest: sha256:2a07a07e5bbf62e7b583cbb5257357c7e0ba1a8e9650e8fa76d999a60968530f
Status: Downloaded newer image for nginx:latest
50f78171d0e26922a0b01025125a231b33615086119ebf3e8a0846b2b297760b
啟動了 web server,就可以用 browser 連結到 http://localhost/ 瀏覽網頁。
使用 Kitematic 管理 docker container
docker for mac 並沒有將 kitematic 放進去,而是讓去下載 Kitematic ,下載後會取得 Kitematic-Mac.zip,直接用以下的指令將 kitematic 解壓縮到 Applications 目錄中。
sudo unzip ~/Downloads/Kitematic-Mac.zip -d /Applications
我們可以看到剛剛執行的那個 ubuntu 的 container。
一些 docker 的基本指令
docker 容器有兩種,一種是 interactive container,
可以用互動的方式進行操作,一種是 daemonized container,可以執行應用服務 server。
執行一個名稱為 u1 的容器,-i 是保證容器有打開 STDIN,-t 是產生一個虛擬的 tty 終端,此容器使用 ubuntu 這個 image,然後執行 bash,在 bash shell 中以 exit 離開時,這個容器就會停止。可以在 bash 中執行 ls, ps -aux 等等指令。
docker run --name u1 -i -t ubuntu /bin/bash
啟動剛剛建立的 u1 container
重新附著到 u1,也就是進入 u1 的 bash
利用 while,產生一個一直列印 hello world 的 daemon server
docker run --name d1 -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"
查看正在執行的 container
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a0d673b448a8 ubuntu "/bin/sh -c 'while tr" About a minute ago Up About a minute d1
列出所有的 containers,包含執行跟停止中的
查看 d1 的 log
docker logs d1
# 類似 tail -f
docker logs -f d1
停止 d1
產生一個新的 d2 container(新的容器名稱不能跟舊的一樣),不管遇到容器的(bash)退出碼是多少,都會自動重新啟動,也可以用 --restart=on-failure:5 來限制退出碼非0時,重新啟動,且只會重啟動 5 次。
docker run --restart=always --name d2 -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"
可用 inspect 查看 d2 的資訊
$ docker inspect d2
[
{
"Id": "8dba5cf2a6ef94143f460bb6be37342d0e14bdae3d3d02645736ab1c9f343621",
"Created": "2016-12-22T01:57:38.837925059Z",
"Path": "/bin/sh",
"Args": [
"-c",
"while true; do echo hello world; sleep 1; done"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 2799,
"ExitCode": 0,
"Error": "",
"StartedAt": "2016-12-22T01:57:39.765031056Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:104bec311bcdfc882ea08fdd4f5417ecfb1976adea5a0c237e129c728cb7eada",
"ResolvConfPath": "/var/lib/docker/containers/8dba5cf2a6ef94143f460bb6be37342d0e14bdae3d3d02645736ab1c9f343621/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/8dba5cf2a6ef94143f460bb6be37342d0e14bdae3d3d02645736ab1c9f343621/hostname",
"HostsPath": "/var/lib/docker/containers/8dba5cf2a6ef94143f460bb6be37342d0e14bdae3d3d02645736ab1c9f343621/hosts",
"LogPath": "/var/lib/docker/containers/8dba5cf2a6ef94143f460bb6be37342d0e14bdae3d3d02645736ab1c9f343621/8dba5cf2a6ef94143f460bb6be37342d0e14bdae3d3d02645736ab1c9f343621-json.log",
"Name": "/d2",
"RestartCount": 0,
"Driver": "aufs",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "",
"ExecIDs": null,
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "default",
"PortBindings": {},
"RestartPolicy": {
"Name": "always",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": null,
"CapAdd": null,
"CapDrop": null,
"Dns": [],
"DnsOptions": [],
"DnsSearch": [],
"ExtraHosts": null,
"GroupAdd": null,
"IpcMode": "",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"SecurityOpt": null,
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "runc",
"ConsoleSize": [
0,
0
],
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": null,
"BlkioDeviceReadBps": null,
"BlkioDeviceWriteBps": null,
"BlkioDeviceReadIOps": null,
"BlkioDeviceWriteIOps": null,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": [],
"DiskQuota": 0,
"KernelMemory": 0,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": -1,
"OomKillDisable": false,
"PidsLimit": 0,
"Ulimits": null,
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0
},
"GraphDriver": {
"Name": "aufs",
"Data": null
},
"Mounts": [],
"Config": {
"Hostname": "8dba5cf2a6ef",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"no_proxy=*.local, 169.254/16",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/sh",
"-c",
"while true; do echo hello world; sleep 1; done"
],
"Image": "ubuntu",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "ea7a45d1dcfb681283e18aad9feaaedd98da8cc8cd4155ca2284e4aeb6db640f",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {},
"SandboxKey": "/var/run/docker/netns/ea7a45d1dcfb",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "48480731d5cb9ab9e268e341c65fd924671222660ffa58a6adc61734811bae83",
"Gateway": "172.17.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"MacAddress": "02:42:ac:11:00:02",
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "b7f0ca41f518b121beb774aac41e709b5032e7cff96939e43e3ddac1ed16cf98",
"EndpointID": "48480731d5cb9ab9e268e341c65fd924671222660ffa58a6adc61734811bae83",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:02"
}
}
}
}
]
配合 format 查看特定的資訊
$ docker inspect --format='{{ .State.Running }}' d2
true
$ docker inspect --format='{{ .NetworkSettings.IPAddress }}' d2
172.17.0.2
$ docker inspect --format='{{.Name}} {{ .State.Running }}' d1 d2
/d1 false
/d2 true
先停止 d2 後,才能用 rm 刪除 d2
docker stop d2
docker rm d2
列出所有 images
取得 ubuntu 的 image
以 TAG 指定 ubuntu 的版本
docker run --name t1 -t -i ubuntu:12.04 /bin/bash
列出 ubuntu 的 images
$ docker images ubuntu
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 104bec311bcd 6 days ago 129 MB
ubuntu 12.04 f0d07a756afd 6 days ago 103.6 MB
要先移除跟 image 相關的 containers,才能移除 image
docker rm t1
docker rmi ubuntu:12.04
搜尋所有 docker hub 的公用可用 image
建立 docker image
有兩種方式:
使用 docker commit: 不建議用這種方式
登入 docker hub
$docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: yaocl
Password:
Login Succeeded
執行一個 ubuntu 的 container
docker run -i -t ubuntu /bin/bash
在容器中安裝 apache
apt-get -yqq update
apt-get -y install apache
exit
將 container 以 commit 存成另一個 image
$ docker commit fd915f899feb yaocl/apache2
sha256:32421625c13d3076c90a18d3146dab180a9ed5c33e8439b5572acdcf7a328e02
$ docker images yaocl/apache2
REPOSITORY TAG IMAGE ID CREATED SIZE
yaocl/apache2 latest 32421625c13d 31 seconds ago 267.6 MB
自訂一些資訊欄位,產生新的 image tag
$ docker commit -m="custom image" --author="yaocl" fd915f899feb yaocl/apache2:webserver
sha256:b2e0f5faed623a8f62bdad08223773ae85274c9fc40e12753ac1df1258152f2f
$ docker images yaocl/apache2
REPOSITORY TAG IMAGE ID CREATED SIZE
yaocl/apache2 webserver b2e0f5faed62 About a minute ago 267.6 MB
yaocl/apache2 latest 32421625c13d 2 minutes ago 267.6 MB
使用新的 yaocl/apache2:webserver image
docker run -t -i yaocl/apache2:webserver /bin/bash
使用 dokcer build 及 Dockerfile 文件
static_web 目錄就是 dokcer 的 build environment,此環境就稱為 context 或 build context,先在這個 context 中建立一個 Dockerfile
mkdir static_web
cd static_web/
touch Dockerfile
編輯 Dockerfile 內容
# Version 0.0.1
FROM ubuntu:latest
MAINTAINER yaocl
ENV REFRESHED_AT 2016/12/22
RUN apt-get update
RUN apt-get install -y nginx
RUN echo "hi from container" > /usr/share/ngnix/html/index.html
EXPOSE 80
以 build 指令建立 image,如果沒有寫 :v1,就是預設的 latest 這個 tag
cd static_web/
#docker build -t="yaocl/static_web" .
docker build -t="yaocl/static_web:v1" .
建立 images 時發生錯誤
Setting up nginx (1.4.6-1ubuntu3.7) ...
Processing triggers for libc-bin (2.19-0ubuntu6.9) ...
Processing triggers for sgml-base (1.26+nmu4ubuntu1) ...
---> 83f7d5f1ad20
Removing intermediate container 2681f1e39e14
Step 5 : RUN echo "hi from container" > /usr/shre/ngnix/html/index.html
---> Running in f1bd373dc64e
/bin/sh: 1: cannot create /usr/shre/ngnix/html/index.html: Directory nonexistent
The command '/bin/sh -c echo "hi from container" > /usr/shre/ngnix/html/index.html' returned a non-zero code: 2
以 docker images 可以看到錯誤步驟前那一個 image id: 83f7d5f1ad20
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 83f7d5f1ad20 39 seconds ago 231.1 MB
以該 image id 執行一個 container
$ docker run --name test -i -t 83f7d5f1ad20 /bin/bash
發現是路徑寫錯了 /usr/share/nginx/html
修改 Dockerfile 內容
# Version 0.0.1
FROM ubuntu:latest
MAINTAINER yaocl
ENV REFRESHED_AT 2016/12/22
RUN apt-get update
RUN apt-get install -y nginx
RUN echo "hi from container" > /usr/share/nginx/html/index.html
EXPOSE 80
再 build 一次,就成功了
$ docker build -t="yaocl/static_web:v1" .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM ubuntu:latest
---> 3f755ca42730
Step 2 : MAINTAINER yaocl
---> Using cache
---> 062ac75f50aa
Step 3 : RUN apt-get update
---> Using cache
---> 43e1cecf45be
Step 4 : RUN apt-get install -y nginx
---> Using cache
---> 83f7d5f1ad20
Step 5 : RUN echo "hi from container" > /usr/share/nginx/html/index.html
---> Running in 9a1dcbadd174
---> 11fa4a76019d
Removing intermediate container 9a1dcbadd174
Step 6 : EXPOSE 80
---> Running in 2d0db9f90d57
---> 683486e4c00a
Removing intermediate container 2d0db9f90d57
Successfully built 683486e4c00a
[14:10]charley@cmbp ~/Downloads/static_web$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
yaocl/static_web v1 683486e4c00a 4 seconds ago 231.1 MB
由於 docker 會在每一個步驟都產生 image,可以加上 --no-cache 避免產生這個問題
docker build --no-cache -t="yaocl/static_web:v1" .
產生 yaocl/static_web:v1 的 container
$ docker run --name t1 -d -p 80 yaocl/static_web:v1 nginx -g "daemon off;"
4dbe731715cb046c6c12bb23264e9b4add0631948b3dfa15cb4e740a586eed68
以 ps 指令查看主機隨機以 49153 ~ 65535 任意一個 port 對應到 80 的資訊
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4dbe731715cb yaocl/static_web:v1 "nginx -g 'daemon off" 29 seconds ago Up 28 seconds 0.0.0.0:32768->80/tcp t1
瀏覽器可以連結到 http://localhost:32768 查看網頁
如果是 -p 80:80 就是使用主機的 80
$ docker run --name t1 -d -p 80:80 yaocl/static_web:v1 nginx -g "daemon off;"
綁定主機的 127.0.0.1:80
$ docker run --name t1 -d -p 127.0.0.1:80:80 yaocl/static_web:v1 nginx -g "daemon off;"
Dockerfile 指令
Dockerfile reference
CMD
指定 container 最後要執行的命令,也可以用 docker run 覆蓋 CMD 的指令
CMD ["nginx", "-g", "daemon off;"]
可以省略最後用 /bin/sh -c 執行的指令
$ docker run --name t2 -d -p 127.0.0.1:80:80 yaocl/static_web:v2
ENTRYPOINT
跟 CMD 類似但不一樣,使用 ENTRYPOINT 可以接受 run 後面增加的參數
ENTRYPOINT ["/usr/sbin/nginx"]
執行時會增加後面 -g "daemon off;" 的參數給 nginx
$ docker run --name t2 -d -p 127.0.0.1:80:80 yaocl/static_web:v2 -g "daemon off;"
所以實際上使用會是
ENTRYPOINT ["/usr/sbin/nginx"]
CMD ["-g", "daemon off;"]
或是在沒有參數時,列印 help 資訊
ENTRYPOINT ["/usr/sbin/nginx"]
CMD ["-h"]
WORKDIR
在 container 內設定工作目錄,執行 ENTRYPOINT 或 CMD
使用時可以在過程中隨時切換目錄
WORKDIR /opt/webapp/db
RUN bundle install
WORKDIR /opt/webapp
ENTRYPOINT ["rackup"]
ENV
設定環境變數,可以在 docker run 以 -e 參數指定環境變數
ENV RVM_PATH /home/rvm/
ENV TARGET_DIR /opt/app
WORKDIR $TARGET_DIR
docker run -i -t -e "WEB_PORT=8080" ubuntu env
USER
指定用什麼帳號身份執行指令,可以用 docker run 的 -u 參數覆蓋這個設定,如果沒有指定,預設為 root
USER user
USER user:group
USER uid
USER uid:gid
USER uid:group
VOLUME
對 container 增加 volume,一個 volume 可同時存在於一個或多個 container 的特定目錄,該目錄可繞過聯合文件系統,進行資料共享或持久化的工作
- volume 可在 container 之間共享, 重用
- container 不一定要跟其他 container 共享 volume
- 對 volume 修改資料會立即生效
- 修改 volume 裡面的資料,不會對 image 產生影響
- volume 會一直存在,直到沒有任何 container 使用它
VOLUME ["/opt/project", "/data"]
ADD
將建構環境中的檔案複製到 image 中,後面的目的地檔名不能省略,新目錄或檔案權限都是 0755,UID及 GID 都是 0
ADD sw.lic /opt/application/sw.lic
ADD http://wordpress.org/latest.zip /root/wordpress.zip
這會自動將 latest.tar.gz 解壓縮到目的地的目錄中
ADD latest.tar.gz /var/www/wordpress/
COPY
類似 ADD,但沒有解壓縮的功能
COPY conf.d/ /etc/apache2/
ONBUILD
為 image 增加 trigger 的功能,當 image 被使用時,就會觸發 trigger
ONBUILD ADD . /app/src
ONBUILD RUN cd /app/src && make
Docket hub 與私有的 Docker Registry
push image to Docker hub
也可以在 docket hub 增加 github/BitBucket 帳號連結,然後製作 Automated Build 自動產生 image
用這個指令可以產生一個 private docker registry
docker run -p 5000:5000 registry
$ docker images yaocl/static_web
REPOSITORY TAG IMAGE ID CREATED SIZE
yaocl/static_web v2 1d48b7f306d8 2 hours ago 231.1 MB
yaocl/static_web v1 ce19993fc181 3 hours ago 231.1 MB
將 image push 到 docker.example.com:5000 這個 private registry
docker tag 1d48b7f306d8 docker.example.com:5000/yaocl/static_web
docker run -t -i docket.example.com:5000/yaocl/static_web /bin/bash
scripts
移除所有 stopped containers
docker rm $(docker ps -a -q)
移除所有 UNTAG images
docker rmi $(docker images | grep "<none>" | awk '{print $3}')
References
[Docker] 在 Mac 上使用原生的 Docker for Mac 操作 container
撰寫一份符合需求的 Dockerfile