容器基礎4:重識docker容器
docker容器實際案例
1.使用docker部署python寫的web應用
from flask import Flask import socket import os app = Flask(__name__) @app.route('/') def hello(): html = "<h3>Hello {name} </h3><br>Hostname:</b> {hostname}<br/>" return html.format(name=os.getenv("NAME","world"),hostname=socket.gethostname()) if __name__ == "__main__": app.run(host='0.0.0.0',port=80)
$ cat requirements.txt
Flask
2.製作容器映象
使用Dockerfile製作docker映象,也就是rootfs
# 使用官方提供的Python開發映象作為基礎映象 FROM python:2.7-slim # 將工作目錄切換為/app WORKDIR /app # 將當前目錄下的所有內容複製到/app下 ADD ./app # 使用pip命令安裝這個應用所需要的依賴 RUN pip install --trusted-host pypi.python.org -r requirements.txt # 允許外接訪問容器的80埠 EXPOSE 80 # 設定環境變數 ENV NAME world # 設定容器程序為:python app.py 即:這個Python應用程式的啟動命令 CMD ["python","app.py"]
3.Dockerfile設計思想
使用標準原語,(大寫高亮的詞語),描述我們要構建的Docker映象。並且這些原語,都是按順序處理的
FROM原語:指定"python:2.7-slim"這個官方維護的映象,從而免去安裝Python等語言環境的操作
RUN原語: 容器裡執行shell命令的意思
WORKDIR:dockerfile後面的操作都以這一句指定的/app目錄作為當前目錄
CMD: dockerfile指定python app.py為這個容器的程序,這裡app.py的實際路徑是/app/app.py
所以CMD["python","app.py"]等價於 docker run python app.py
ENTRYPOINT:它和CMD都是docker容器裡程序啟動所必須的引數,完整執行格式"ENTRYPOINT CMD"
(不寫,預設是/bin/sh -c,所以實際執行的是/bin/sh -c "python ap.py",cmd的內容就是ENTRYPOINT 引數)
4.dockerfile 存放位置
Dockerfile app.py requirements.txt
5.製作docker映象
$ docker build -t helloworld .
-t:映象加Tag,docker build 會自動載入當前目錄下的Dockerfile檔案,按順序指定原語
過程:docker使用基礎映象啟動了一個容器,在容器中依次執行Dockerfile中的原語
注意事項:
docker每個原語執行後,都會生成一個對應的映象層,即使原語本身沒有明顯的修改檔案操作(env),對應的層也會存在,只不過外界看這個層是空的
6.檢視docker映象
$ docker image ls
RESPOSITORY TAG IMAGE ID
helloworld latest 653287cdf998
7.啟動容器
docker run -p 4000:80 helloworld
-p 4000:80告訴docker,把容器內的80埠對映在宿主機的4000埠上
映象名helloworld,什麼都沒寫,dockerfile中已經指定了CMDB。否組就需要些程序的啟動命令
docker run -p 4000:80 helloworld python app.py
8.檢視容器
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED
4ddf4638572d helloworld "python app.py" 10 seconds ago
發現容器啟動了
驗證一下
$ curl http://localhost:4000
<h3>Hello World!</h3><b>Hostname:</b> 4ddf4638572d<br/>
9.映象上傳DockerHub
註冊Docker Hub賬號,docker login登入
給映象起一個完整的名字geektime/helloworld:v1
geektime是賬戶名
$ docker tag helloworld geektime/helloworld:v1
$ docker push geektime/helloworld:v1
10.將正在執行的容器,直接轉為一個映象
這個容器執行起來後,我又在裡面做了一些操作,並且要把操作結果儲存到映象裡
將容器4ddf4638572d 提交為映象geektime/helloworld:v2
$ docker commit 4ddf4638572d geektime/helloworld:v2
11.docker exec 怎麼進入容器裡的呢?
程序的namespace資訊在宿主機上是真實存在的,以檔案方式存在
如下命令,可以看到docker容器在宿主機上的程序號是25686
$ docker inspect --format '{{ .State.Pid }}' 4ddf4638572d
25686
可以檢視宿主機的proc檔案,看到程序25686的所有namespace對應檔案(ns是namespace的簡寫)
$ ls -l /proc/25686/ns
total 0
lrwxrwxrwx 1 root root 0 Aug 13 14:05 cgroup -> cgroup:[4026531835]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 ipc -> ipc:[4026532278]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 mnt -> mnt:[4026532276]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 net -> net:[4026532281]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 pid -> pid:[4026532279]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 pid_for_children -> pid:[4026532279]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 uts -> uts:[4026532277]
每種namespace,在ns目錄下都對應一個虛擬檔案,並連線到真實的namespace檔案上
這樣就可以實現,將一個程序加入到一個已經存在的namespace中。(就實現了進入到程序所在容器的目的,這就是docker exec原理)