摘要:Docker
容器概念基礎
容器是一個精簡版的操作系統,一般一個容器只運行一個應用,容器通過鏡像創建,使用 docker run
命令創建,容器起到了隔離
作用,容器和容器之間獨享空間和網絡等
容器的基本操作
容器的基本操作包括創建(啟動),停止,重啟,查看,檢查等,容器通過鏡像創建,使用docker run
命令創建,需要指定run參數,鏡像名,容器執行命令,語句格式如下
docker run [OPTIONS] IMAGE [COMMAND]
在實際使用中啟動一個鏡像,例如
root@ubuntu:~# docker run --rm -p 5000:5000 --name container_test -d xiaogp/my_image_test:v1
6954e5372fbec11576afa8213d8810aafdc7aa2141242fc5c8cb57656327b123
(1)常用參數如下
-
-d
: 后臺運行容器,并返回容器ID -
-i
: 以交互模式運行容器,通常與 -t 同時使用 -
-p
: 指定端口映射,格式為宿主機端口:容器端口
-
-t
: 為容器重新分配一個偽輸入終端,通常與 -i 同時使用 -
--name
: 為容器指定一個名稱 -
-e
,--env
: 設置環境變量 -
-w
,--workdir
: 指定容器的工作目錄 -
--env-file
: 從指定文件讀入環境變量 -
--net
: 指定容器的網絡連接類型,支持 bridge,host,none,container四種類型,其中host代表容器使用主機的網絡,可以不加-p指定端口映射 -
--volume
,-v
: 綁定一個卷,可以將宿主機的文件或數據掛載到容器運行,掛載的是一個本地目錄,掛載到宿主機目錄,而不是文件,掛載的容器目錄的任何改動將會同步到宿主機被掛載的目錄下 -
--rm
:容器在停止后自動刪除容器 -
--restart
: 指定容器停止后的重啟策略,默認不重啟 -
--entrypoint
:覆蓋鏡像的入口點
(2)run -e覆蓋Docker ENV
-e
設置環境變量,格式是-e k1=v1 -e k2=v2
,使得在docker鏡像中的程序能夠直接訪問到環境變量,同時可以作為配置參數放在docker run啟動鏡像的時候設置,而不是寫死在dockerfile在build的過程中,-e和dockerfile中的ENV
變量作用相同,當變量重名時-e替換ENV,下面測試一些-e參數,在Dockerfile指定環境變量
FROM ubuntu
ENV a="12a"
直接構建成容器
root@ubuntu:~/myproject# docker build -t xiaogp/test_env .
開啟一個終端啟動容器內部,打印指定的環境變量a
root@ubuntu:~/myproject# docker run -it xiaogp/test_env /bin/bash
root@ubuntu:/# echo $a
12a
此時在run指令中增加-e設置環境變量,可見-e替換了Dockerfile中指定的環境變量
root@ubuntu:~/myproject# docker run -it -e a=a12 xiaogp/test_env /bin/bash
root@ubuntu:/# echo $a
a12
因為一個鏡像可以啟動多個容器,所以可以通過設置不同-e達到設置不同配置參數的目的,比如下一個例子在Dockerfile中設置和將環境變量寫入yaml文件再供Python調用,執行的內容為打印yaml配置文件的參數內容,比如下面這個例子先看下目錄結構
root@ubuntu:~/docker_test/env_test# tree
.
├── config.yml
├── Dockerfile
├── run.sh
└── test.py
其中config.yml是一個空配置文件,在run.sh中先使用echo寫入追加配置參數到config.yml在執行Python腳本
#!/bin/bash
echo "host: $host" > config.yml
echo "port: $port" >> config.yml
echo "dbname: $dbname" >> config.yml
python test.py
Dockerfile中啟動run.sh腳本作為容器執行命令
FROM python:3.7
ENV PIPURL=https://mirrors.aliyun.com/pypi/simple/
RUN pip install pyyaml -i ${PIPURL} --default-timeout=1000
COPY . .
CMD ["bash", "run.sh"]
在啟動容器時,使用-e指定環境變量,在run.sh中echo將環境變量拿到和寫入配置文件,測試多次以不同的配置參數啟動容器如下
root@ubuntu:~/docker_test/env_test# docker run -e host=192.168.60.3 -e port=3333 -e dbname=test --rm 30cd81830829
{'host': '192.168.60.3', 'port': 3333, 'dbname': 'test'}
root@ubuntu:~/docker_test/env_test# docker run -e host=192.168.60.9 -e port=3333 -e dbname=test --rm 30cd81830829
{'host': '192.168.60.9', 'port': 3333, 'dbname': 'test'}
(3)run -v覆蓋Docker COPY / ADD
-v
設置掛載運行,將宿主機當前目錄下的文件掛載到容器中/home目錄下,例如
root@ubuntu:~# docker run --rm -p 5000:5000 --name container_test -d -v `pwd`:/home xiaogp/my_image_test:v1
如果掛載的目錄和Dockerfile中的COPY的目錄不一致,-v會替代COPY或者ADD,例如現在Docker中COPY一個文件到容器/home目錄下
FROM ubuntu
COPY . /home
WORKDIR /home
CMD ["bash", "start.sh"]
同目錄下start.sh內容是打印1
#!/bin/bash
echo 1
構建鏡像結束后,指定-v啟動,起始掛載另外一個目錄,目錄下start.sh內容是打印2
#!/bin/bash
echo 2
root@ubuntu:~/docker_test# docker run -v `pwd`:/home xiaogp/volume_test
2
(4)run command覆蓋Dockerfile CMD
docker run參數中最后的COMMAND會覆蓋Dockerfile中指定的CMD
,例如執行echo 2替換原始Dockerfile中的CMD echo 1,輸出結果是2且執行完畢后退出
# Dockerfile echo_test
FROM ubuntu
CMD echo 1
root@ubuntu:~# docker run xiaogp/echo_test echo 2
2
(5)run --entrypoint覆蓋Dockerfile ENTRYPOINT
對于Dockerfile中的ENTRYPOINT
指定的啟動命令docker run的COMMAND不會覆蓋,如果要覆蓋Docker中的ENTRYPOINT需要指定docker run中的--entrypoint
參數,格式是
docker run --entrypoint [new_command] [docker_image] [optional:value]
測試一個Dockerfile輸出1
# Dockerfile echo_test:v1
FROM ubuntu
ENTRYPOINT echo 1
在docker run中使用--entrypoint覆蓋Dockerfile中的ENTRYPOINT
root@ubuntu:~/docker_test/entrypoint_test# docker run --rm --entrypoint /bin/echo xiaogp/entrypoint_test 2
2
root@ubuntu:~/docker_test/entrypoint_test# docker run --rm --entrypoint /bin/echo xiaogp/entrypoint_test 3
3
(6)其他查看容器的操作
容器啟動后通過docker ps
或者docker container ls
查看容器,可以增加額外參數比如-a
顯示所有容器,默認只顯示運行的容器,可以增加--no-trunc
參數使得顯示結果不截斷,例如
root@ubuntu:~# docker ps -a --no-trunc
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c975fa6d0eb51e0ac739c96038cc3d7b731d8991cf98a50a6180f4a27538af84 xiaogp/my_image_test:v1 "gunicorn -c gun.conf.py manage:app" 58 seconds ago Exited (0) 16 seconds ago container_test
顯示結果分別顯示了容器的ID,鏡像,執行命令,創建時間,狀態,端口映射(宿主機->容器)和容器名稱。對于已經運行的容器可以使用docker stop
停止,如果在docker run時增加--rm參數則停止的容器保留不會自動刪除,例如
root@ubuntu:~# docker stop c97
c97
除了docker stop命令還有一種停止容器的命令docker kill
,相比于docker stop,docker kill是強制立即停止
,而docker stop是先給了容器10秒(默認)的時間,使得容器有一定的時間處理、保存程序執行現場,優雅的退出程序
,例如
root@ubuntu:~# docker kill c97
c97
在容器停止之后可以使用docker start
再啟動一個停止的容器,例如
root@ubuntu:~# docker start c97
c97
除此之外可以使用docker restart
,此時容器可以使停止的也可以是在運行中的,例如
root@ubuntu:~# docker restart c97
c97
查看容器詳情
查看容器詳情使用docker inspect
,比如
root@ubuntu:~/docker_test/entrypoint_test# docker inspect 3b2aa935026f
[
{
"Id": "3b2aa935026fa2e5d2045e42cb9f0af58152b42cb6bfc5fd81a15df150328e7f",
"Created": "2021-06-27T01:34:11.127764986Z",
"Path": "gunicorn",
"Args": [
"-c",
"gun.conf.py",
"manage:app"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 32559,
"ExitCode": 0,
"StartedAt": "2021-06-27T01:34:16.963985809Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:f9399fca099a36ecd70e9a23e20567e83a437726de641c457002c9dd96efc2c9",
"Name": "/crazy_hamilton",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"HostConfig": {
"Config": {
"Hostname": "3b2aa935026f",
"Domainname": "",
"User": "",
"ExposedPorts": {
"5000/tcp": {}
},
"Env": [
"PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"LANG=C.UTF-8",
"GPG_KEY=0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D",
"PYTHON_VERSION=3.7.9",
"PYTHON_PIP_VERSION=20.3.3",
"PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/5f38681f7f5872e4032860b54e9cc11cf0374932/get-pip.py",
"PYTHON_GET_PIP_SHA256=6a0b13826862f33c13b614a921d36253bfa1ae779c5fbf569876f3585057e9d2",
"PIPURL=https://pypi.tuna.tsinghua.edu.cn/simple"
],
"Cmd": [
"gunicorn",
"-c",
"gun.conf.py",
"manage:app"
],
"Image": "xiaogp/my_image_test:v1",
"Volumes": null,
"WorkingDir": "/home",
"Entrypoint": null,
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "46b1e6ae164366966b82cb70097bee4f37943e7aefb29882c954fea68680d608",
"HairpinMode": false,
"Ports": {
"5000/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "5000"
}
]
}
}
}
]
在以上截取的內容中展示了容器詳情,包括容器id,創建時間,執行命令和參數,執行狀態,容器pid,落腳點,環境變量,網絡設置,端口映射等,也可以使用Go語言風格輸出指定的詳情,比如分別只看容器的pid和容器的執行命令
root@ubuntu:~/docker_test/entrypoint_test# docker inspect -f {{".State.Pid"}} 3b2aa935026f
32559
root@ubuntu:~/docker_test/entrypoint_test# docker inspect -f {{".Config.Cmd"}} 3b2aa935026f
[gunicorn -c gun.conf.py manage:app]
進入容器
容器是一個操作系統,可以進入這個操作系統查看容器的運行情況,有多種方式進入容器,其中主要是使用docker exec
進入容器,在一個運行中的容器中執行一個命令,使用-it
并帶有/bin/bash
命令就可以進入容器,比如
root@ubuntu:~/docker_test/entrypoint_test# docker run -p 5000:5000 -d xiaogp/my_image_test:v1
3b2aa935026fa2e5d2045e42cb9f0af58152b42cb6bfc5fd81a15df150328e7f
root@ubuntu:~/docker_test/entrypoint_test# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3b2aa935026f xiaogp/my_image_test:v1 "gunicorn -c gun.con…" 9 seconds ago Up 3 seconds 0.0.0.0:5000->5000/tcp crazy_hamilton
root@ubuntu:~/docker_test/entrypoint_test# docker exec -it crazy_hamilton /bin/bash
root@3b2aa935026f:/home# ls
Dockerfile __pycache__ blueprints docker_run.sh gun.conf.py logs models.py run.sh templates
PiraScore app data extensions.py gunicorn.pid manage.py requirements.txt settings.py utils.py
除了/bin/bash也可以是其他命令掛載exec后面則可以直接對一個運行中的容器執行命令,比如查看容器的進入落腳點路徑,容器中的內存情況
root@ubuntu:~/docker_test/entrypoint_test# docker exec -it crazy_hamilton pwd
/home
root@ubuntu:~/docker_test/entrypoint_test# docker exec -it crazy_hamilton free
total used free shared buff/cache available
Mem: 7901132 2280048 3446664 411924 2174420 4913372
Swap: 2097148 0 2097148
查看容器日志
當容器以后臺-d
運行時,日志運行在容器內部,可以進入容器內部查看日志,也可以使用docker logs
查看日志,以一個flask api接口的容器為例,日志寫入文件,同時也會輸出在flask的控制臺
import logging
import json
import datetime
import traceback
from flask import Flask, jsonify, request
from logging.handlers import RotatingFileHandler
from logging import StreamHandler
app = Flask(__name__)
app.logger.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s [%(module)s] %(levelname)s %(message)s', '%Y-%m-%d %H:%M:%S')
rotating_handler = RotatingFileHandler('logs/detail.log', mode="a", maxBytes=1024 * 1024 * 100, encoding="utf8")
rotating_handler.setLevel(logging.INFO)
rotating_handler.setFormatter(formatter)
# 分別輸出控制臺和本地文件
app.logger.addHandler(rotating_handler)
@app.route("/search", methods=["POST"])
def api():
try:
data = request.get_json(force=True)
ent = data["ent"]
dt = data.get("dt", datetime.datetime.today().strftime("%Y-%m-%d"))
res = {"msg": "success", "code": 200, "data": json.dumps({"ent": ent, "dt": dt}, ensure_ascii=False)}
code = 200
except Exception as e:
res = {"meg": "fail", "code": 400, "trace": traceback.format_exc()}
code = 400
app.logger.info(res)
return jsonify(res), code
if __name__ == '__main__':
app.run("0.0.0.0", 5000, debug=False)
創建Dockerfile以及構建鏡像,啟動容器
root@ubuntu:~/docker_test/docker_logs_test# cat Dockerfile
FROM python:3.7
MAINTAINER xiaogp
ENV PIPURL=https://mirrors.aliyun.com/pypi/simple/
RUN pip install flask -i ${PIPURL}
WORKDIR /home
COPY . .
CMD ["python", "api.py"]
root@ubuntu:~/docker_test/docker_logs_test# docker bulid -t xiaogp/docker_logs .
root@ubuntu:~/docker_test/docker_logs_test# docker run -p 5000:5000 -d xiaogp/docker_logs
啟動一個腳本不斷請求api接口
import time
import uuid
import requests
while True:
res = requests.post("http://127.0.0.1:5000/search", json={"ent": uuid.uuid4().hex})
time.sleep(3)
進入容器內部查看日志
root@ubuntu:~/docker_test/docker_logs_test# docker exec -it d8020c4f99bb /bin/bash
root@d8020c4f99bb:/home# ls
Dockerfile api.py logs
root@d8020c4f99bb:/home# cd logs/
root@d8020c4f99bb:/home/logs# ls
detail.log
root@d8020c4f99bb:/home/logs# more detail.log
2021-06-29 07:55:56 [api] INFO {'msg': 'success', 'code': 200, 'data': '{"ent": "8761c66441d24a2a910be856e68298d4", "dt": "2021-06-29"}'}
2021-06-29 07:55:59 [api] INFO {'msg': 'success', 'code': 200, 'data': '{"ent": "18bf77d79b10492b9406c56263247c5b", "dt": "2021-06-29"}'}
2021-06-29 07:56:02 [api] INFO {'msg': 'success', 'code': 200, 'data': '{"ent": "68188ba02154436fa861cd11f5027699", "dt": "2021-06-29"}'}
root@d8020c4f99bb:/home/logs#
另一種方式是直接使用docker logs
命令,比如使用-f
追蹤輸出,并且從最后的第1行開始輸出
root@ubuntu:~/docker_test/docker_logs_test# docker logs -f -n 1 d8020c4f99bb
172.17.0.1 - - [29/Jun/2021 08:01:13] "POST /search HTTP/1.1" 200 -
[2021-06-29 08:01:16,190] INFO in api: {'msg': 'success', 'code': 200, 'data': '{"ent": "8c3851e0c3ca4c0fbb7dbd245f8deb0f", "dt": "2021-06-29"}'}
172.17.0.1 - - [29/Jun/2021 08:01:16] "POST /search HTTP/1.1" 200 -
[2021-06-29 08:01:19,204] INFO in api: {'msg': 'success', 'code': 200, 'data': '{"ent": "39d775d3a32c4cd19f9f1a836a377ece", "dt": "2021-06-29"}'}
172.17.0.1 - - [29/Jun/2021 08:01:19] "POST /search HTTP/1.1" 200 -
此時宿主機的logs目錄下為空,容器中的logs目錄下存在detail.log文件,如果使用-v
將宿主機目錄掛載到容器作為容器寫入的目錄,則容器中數據的變動會同步到本地,這樣可以直接在本地查看日志,修改容器啟動為-v
掛載的形式
root@ubuntu:~/docker_test/docker_logs_test# docker run -p 5000:5000 -d -v `pwd`:/home xiaogp/docker_logs
此時本地logs目錄下開始產生日志,且這個日志和容器內的logs目錄下一致
root@ubuntu:~/docker_test/docker_logs_test/logs# cat detail.log
2021-06-29 08:07:36 [api] INFO {'msg': 'success', 'code': 200, 'data': '{"ent": "2dd127e80a6246ee8e106961147bfee6", "dt": "2021-06-29"}'}
2021-06-29 08:07:39 [api] INFO {'msg': 'success', 'code': 200, 'data': '{"ent": "666d348b7635417eb55f2771ab33c248", "dt": "2021-06-29"}'}
2021-06-29 08:07:42 [api] INFO {'msg': 'success', 'code': 200, 'data': '{"ent": "76ec19a0fc1a4e12ba82292f3fc087e4", "dt": "2021-06-29"}'}
2021-06-29 08:07:45 [api] INFO {'msg': 'success', 'code': 200, 'data': '{"ent": "f9eb6bd15a50431e9771c2257b486a16", "dt": "2021-06-29"}'}
2021-06-29 08:07:48 [api] INFO {'msg': 'success', 'code': 200, 'data': '{"ent": "59c66d8688574991a3cae021c646bba5", "dt": "2021-06-29"}'}
容器修改生成鏡像
如果容器內的內容改變了,此時刪除容器從鏡像重新啟動容器則改動的內容將不會存在,如果相對修改過的容器保留下來則可以從容器生成新的鏡像,先測試以下容器內修改在刪除的容器后將不再生效,在已有容器中使用pip安裝Python包
root@ubuntu:~/docker_test/entrypoint_test# docker exec -it 3b2aa935026f /bin/bash
root@3b2aa935026f:/home# pip install pymongo -i $PIPURL
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Collecting pymongo
Downloading https://pypi.tuna.tsinghua.edu.cn/packages/b1/29/c0c8791ba972456f8aa3f027af33206499bc9f52a948e0d9c10909339b3c/pymongo-3.11.4-cp37-cp37m-manylinux2014_x86_64.whl (512 kB)
|████████████████████████████████| 512 kB 7.6 MB/s
Installing collected packages: pymongo
Successfully installed pymongo-3.11.4
此時退出容器,并且刪除容器,最后從鏡像重新生成容器
root@3b2aa935026f:/home# exit
root@ubuntu:~/docker_test/entrypoint_test# docker rm -f 3b2
3b2
root@ubuntu:~/docker_test/entrypoint_test# docker run -d -p 5000:5000 xiaogp/my_image_test:v1
此時進入容器檢查,并不存在pymongo包
root@ubuntu:~# docker exec -it c8b /bin/bash
root@c8bcc65a8513:/home# pip list|grep pymongo
root@c8bcc65a8513:/home#
如果要容器變化保存下來需要以這個新容器生成一個鏡像,使用docker commit
,語法如下
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
以新安裝pymongo的容器為例,對新容器使用docker commmit
root@ubuntu:~# docker exec -it c8bcc65a8513 /bin/bash
root@c8bcc65a8513:/home# pip install pymongo -i $PIPURL
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Collecting pymongo
Downloading https://pypi.tuna.tsinghua.edu.cn/packages/b1/29/c0c8791ba972456f8aa3f027af33206499bc9f52a948e0d9c10909339b3c/pymongo-3.11.4-cp37-cp37m-manylinux2014_x86_64.whl (512 kB)
|████████████████████████████████| 512 kB 4.3 MB/s
Installing collected packages: pymongo
Successfully installed pymongo-3.11.4
root@c8bcc65a8513:/home# exit
exit
root@ubuntu:~#
新生成的鏡像叫做xiaogp/my_image_test:v2
root@ubuntu:~# docker commit -m="pip install pymongo" -a="xiaogp" c8bcc65a8513 xiaogp/my_image_test:v2
sha256:094ab012ea3fc41865ce75647405fd3a5ecc441a267511c7fa12c74a226ad0aa
從新鏡像啟動容器并進入容器查看存在新安裝的pymongo
oot@ubuntu:~# docker exec -it a3f434b153fa /bin/bash
root@a3f434b153fa:/home# pip list|grep pymongo
pymongo 3.11.4
root@a3f434b153fa:/home#