摘要: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#