Docker 快速學習自我挑戰 II Day3


Dockerfile 語法梳理與最佳實踐

Dockerfile 語法

  1. FROM 語法:為了安全,盡量使用官方的 Image 作為 Base Image
    • FROM scratch 製作 Base Image
    • FROM centos 使用 Base Image
    • FROM ubuntu:14.04
  2. Label 語法:Metadata 不可少,可以理解成註釋
    • LABEL maintainer="vincent@fishboneapps.com"
    • LABEL version="1.0"
    • LABEL description="This is the description"
  3. RUN 語法:為了美觀,複雜的 RUN 用反斜線換行!避免無用分層,合併多條命令成一行!
1
2
RUN yum update && yam install -y vim \
python-dev # 反斜線換行
1
2
3
RUN apt-get update && apt-get install -y perl \
pwgen --no-install-recommends && rm -rf \
/var/lib/apt/lists/* # 注意清理 cache
1
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME
  1. WORKDIR 語法:用 WORKDIR,不要用 RUN cd!盡量使用絕對目錄
1
WORKDIR /root
1
2
3
WORKDIR /test # 如果沒有會自動創建 test 目錄
WORKDIR demo
RUN pwd # 輸出結果是 /test/demo
  1. ADD 和 COPY 語法:大部分情況,COPY 比 ADD 好,ADD 除了 COPY 的功能以外,還有額外的解壓縮功能。添加遠端文件/目錄要使用 curl 或者 wget。
1
ADD hello / # 將本地的檔案傳到 Image 裡面
1
ADD test.tar.gz / # 添加到根目錄並解壓
1
2
WORKDIR /root
ADD hello test/ # /root/test/hello
1
2
WORKDIR /root
COPY hello test/ # /root/test/hello
  1. ENV 語法:盡量使用 ENV 增加可維護性
1
2
3
ENV MYSQL_VERSION 5.6 # 設置常量
RUN apt-get install -y mysql-server = "${MYSQL_VERSION}" \
&& rm -rf /var/lib/apt/lists/* # 引用常量
  1. VOLUME 和 EXPOSE 語法:儲存和網路,後面單獨講。
  2. CMD 和 ENTRYPOINT 語法:後面單獨講。
  3. Docker 語法參考網址

RUN vs. CMD vs. ENTRYPOINT

  1. 語法說明
    • RUN:執行命令並創建新的 Image Layer
    • CMD:設置容器啟動後默認執行的命令和參數
    • ENTRYPOINT:設置容器啟動時運行的命令
  2. SHELL 和 Exec 格式
    SHELL 格式
1
2
3
RUN apt-get install -y vim
CMD echo "hello docker"
ENTRYPOINT echo "hello docker"

Exec 格式

1
2
3
RUN [ "apt-get", "install", "-y", vim ]
CMD [ "/bin/echo", "hello docker" ]
ENTRYPOINT [ "/bin/echo", "hello docker" ]
  1. 兩種 Dockerfile 格式
    SHELL 格式
1
2
3
FROM centos
ENV name Docker
ENTRYPOINT echo "hello $name"

Exec 格式

1
2
3
FROM centos
ENV name Docker
ENTRYPOINT [ "/bin/echo", "hello $name" ]
  1. CMD 語法
    • 容器啟動時默認執行的命令
    • 如果 docker run 指定了其它命令,CMD 命令被忽略
    • 如果定義了多個 CMD,只有最後一行會執行
  2. ENTRYPOINT 語法
    • 讓容器以應用程序或者服務的形式運行
    • 不會被忽略,一定會執行
    • 最佳實踐:寫一個 shell 腳本作為 entrypoint
1
2
3
4
5
COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["docker-entrypoint.sh]

EXPOSE 27017
CMD ["mongod"]

鏡像的發佈

  1. docker login 登入 Docker Hub 帳號
  2. docker push fishboneapps/hello-world:latest 上傳到遠端倉庫
  3. docker pull fishboneapps/hello-world 從遠端倉庫拉回本地
  4. 推薦的方式是透過 Github 去做關聯,然後只要維護在 Github 上面的 Dockerfile,最後 Docker Hub 會自動幫我們做 build

搭建私有的 Registry

  1. 如果想要架設私有的 Docker Hub,可以使用 Registry
  2. 安裝 Registry
    docker run -d -p 5000:5000 --restart always --name registry registry:2
  3. 在本地重新 build Image
    docker build -t [ip]:5000/hello-world .
  4. 如果直接 push,會顯示有安全性問題,所要先在 /etc/docker 裡面新增 daemon.json
    vim /etc/docker/daemon.json
1
2
3
{
"insecure-registries": ["[ip]:5000"]
}
  1. 然後再修改 /lib/systemd/system/docker.service,在裡面新增一行 EnvironmentFile
1
2
3
4
5
...
ExecStart=/usr/bin/docerd
EnvironmentFile=-/etc/docker/daemon.json
ExecReload=/bin/kill -s HUP $MAINPID
...
  1. 重啟 docker sudo service docker restart
  2. 上傳到遠端 Registry docker push [ip]:5000/hello-world
  3. 因為 Registry 沒有 Web 介面,所以可以使用 Docker Registry API 來驗證上傳是否成功

Dockerfile 實戰

測試運行 python flask

  1. 新增 app.py
1
2
3
4
5
6
7
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "hello docker"
if __name__ == '__main__':
app.run()
  1. 安裝 python sudo apt-get install -y python2.7
  2. 安裝 python-pip sudo apt-get install -y python-pip
  3. 安裝 flask pip install flask
  4. 如果遇到 locale 問題,可以使用以下指令
    export LC_ALL="en_US.UTF-8"
    export LANGUAGE="en_US.UTF-8"

用 Dockerfile 來封裝 python flask

  1. 新增目錄 mkdir flask-hello-world
  2. 跳轉到目錄裡面,新增 Dockerfile cd flask-hello-world
  3. 新增 Dockerfile vim Dockerfile
1
2
3
4
5
6
7
FROM python:3.6
LABEL maintainer="Fishboneapps<vincent@fishboneapps.com>"
RUN pip install flask
COPY app.py /app/
WORKDIR /app
EXPOSE 5000
CMD ["python", "app.py"]
  1. 將 Dockerfile 製作成 Image
    docker build -t fishboneapps/flask-hello-world .
  2. 運行製作好的 Image
    docker run -d fishboneapps/flask-hello-world

Dockerfile 總結

  1. Dockerfile 編寫分三步驟
    • 第一步(環境):引入庫,安裝套件,環境的搭建
    • 第二步(代碼):通過 COPY 或是 ADD 添加到 Image 裡面
    • 第三步(CMD):運行程序

容器的操作

Docker Exec 命令

  1. docker exec -it [容器id] [執行命令]
    docker exec -it 1f7e35f8a8b6 /bin/bash
  2. 檢查 python 的服務
    ps -ef | grep python
  3. 改成運行 python,就會看到直接進去 python 的 shell 裡面
    docker exec -it 1f7e35f8a8b6 python
  4. 可以在 shell 裡面測試運行 Hello Docker,然後退出
1
2
print ("hello docker")
exit()
  1. 打印出容器的 ip 地址
    docker exec -it 1f7e35f8a8b6 ip a
  2. 停止容器 docker stop [容器id]
    docker stop 1f7e35f8a8b6
  3. 刪除所有停止的容器
    docker rm $(docker ps -aq)
  4. 建立容器並指定名稱,如果不指定名稱的話,Docker 會分配一個名字
    docker run -d --name=demo fishboneapps/flask-hello-world
  5. 停止容器也可以用 name,name 是唯一的
    docker stop demo
  6. 開始容器一樣也可以用 name
    docker start demo
  7. 顯示容器的詳細資訊
    docker inspect demo
  8. 顯示容器的 logs
    docker logs demo
  9. Docker container 命令文檔

Dockerfile 實戰(2)

壓力測試工具 - Stress

  1. 開啟一個 ubuntu 容器
    docker run -it ubuntu:18.04
  2. 安裝 stress
    sudo apt-get update && apt-get install -y stress
  3. 找尋 stress 路徑
    which stress
  4. 查看 stress 命令 stress --help
1
2
3
4
5
6
-v, --verbose      be verbose // 顯示更多資料
-m, --vm N spawn N workers spinning on malloc()/free() // 生成 worker
--vm-bytes B malloc B bytes per vm worker (default is 256MB) // 分配記憶體給 worker,默認 256MB
--vm-stride B touch a byte every B bytes (default is 4096)
--vm-hang N sleep N secs before free (default none, 0 is inf)
--vm-keep redirty memory instead of freeing and reallocating
  1. 測試 stress
    stress --vm 1 --verbose
  2. 分配 500000M 的記憶體給 Worker 會無法分配,因為 Host 機器沒有那麼多記憶體
    stress --vm 1 --vm-bytes 500000M --verbose
  3. 將以上步驟打包成 docker image,新增 Dockerfile
1
2
3
4
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y stress
ENTRYPOINT ['/usr/bin/stress']
CMD []
  1. 製作成 image
    docker build -t fishboneapps/ubuntu-stress .
  2. ENTRYPOINT + CMD[] 的形式,可以在啟動容器時,指定命令參數
    docker run -it fishboneapps/ubuntu-stress --vm 1 --verbose

容器資源限制

  1. 使用剛剛的 ubuntu-stress,只分配 200M 的 memory 給容器,容器會退出,因為記憶體不足
    docker run --memory=200M fishboneapps/ubuntu-stress --vm 1 --verbose --vm-bytes 500M
  2. –cpu-shares 命令,是指定容器的相對權重,開啟 3 個 terminal 進行測試
    • 第一個 termianl 執行 docker run --cpu-shares=10 --name=test1 fishboneapps/ubuntu-stress --cpu 1
    • 第二個 terminal 執行 docker run --cpu-shares=5 --name=test2 fishboneapps/ubuntu-stress --cpu 1
    • 第三個 terminal 執行 top 進行監控
  3. 監控後會發現,cpu 的占比,test1 容器的 cpu 占比會是 test2 容器的 2 倍,總和 cpu 的使用量約為 100%
Share