[docker] 核心知识 - 容器/镜像的管理和操作
想要查看完整的指令,可以通过 docker --help
列举所有的指令,这里会提到一些比较常用的核心指令
查看容器的状态
这个应该是最常用的指令,语法为 docker ps
, ps
为 process status
的缩写,使用方式如下:
# 查看当前正在运行的 process
❯ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ec2f40d48498 mysql "docker-entrypoint.s…" 5 days ago Up 5 days 8080/tcp, 0.0.0.0:3306->3306/tcp, 33060/tcp nice_kirch
# 查看所有的 process,包括已经终止的容器
❯ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
801fe6378b3c ca61c1748 "docker-entrypoint.s…" About a minute ago Exited (137) 32 seconds ago inspiring_wiles
ec2f40d48498 mysql "docker-entrypoint.s…" 5 days ago Up 5 days 8080/tcp, 0.0.0.0:3306->3306/tcp, 33060/tcp nice_kirch
# 查看帮助
❯ docker ps --help
Usage: docker ps [OPTIONS]
List containers
Aliases:
docker container ls, docker container list, docker container ps, docker ps
Options:
-a, --all Show all containers (default shows just running)
-f, --filter filter Filter output based on conditions provided
--format string Format output using a custom template:
'table': Print output in table format with column headers (default)
'table TEMPLATE': Print output in table format using the given Go template
'json': Print in JSON format
'TEMPLATE': Print output using the given Go template.
Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates
-n, --last int Show n last created containers (includes all states) (default -1)
-l, --latest Show the latest created container (includes all states)
--no-trunc Don't truncate output
-q, --quiet Only display container IDs
-s, --size Display total file sizes
结束&重新启动容器
结束容器进程
这个没有具体细说过,不过前面有用,语法为 docker stop
,为 docker container stop [OPTIONS] CONTAINER [CONTAINER...]
的缩写
开始容器
即 docker start
之前用的是 docker run
,不过 docker run
后面跟着的是镜像,而非容器,具体语法为 docker container run [OPTIONS] IMAGE [COMMAND] [ARG...]
, docker run
为 alias 缩写。使用 docker run
会新建一个容器去运行当前镜像。
有些情况下,docker run
并不一定是一个最好的解决方案,尤其是在想要继续运行一个终止的容器,这时候可以使用 docker start
去开始已经终止的容器,使用方式如下:
# restart the container, but it will not block the process, unlike using docker run
# ca61c1748 已经关闭了
❯ docker start inspiring_wiles
inspiring_wiles
❯ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
801fe6378b3c ca61c1748 "docker-entrypoint.s…" 3 minutes ago Up 2 seconds 0.0.0.0:3000->80/tcp inspiring_wiles
ec2f40d48498 mysql "docker-entrypoint.s…" 5 days ago Up 5 days 8080/tcp, 0.0.0.0:3306->3306/tcp, 33060/tcp nice_kirch
重新开始容器
即 docker restart
restart
和 start
最大的区别在于 restart
会先执行一个 stop
的操作
如果容器运行已经终止,那么二者没有区别
attached & detached 容器
之前没截图,不过在运行 docker run
的时候,docker 会锁定当前终端,无法进行下一步的操作,但是使用 docker start
就不会发生这种情况。这是因为:
-
前者在 attached 的模式下运行,这个模式下 docker 会在终端输出当前 container 的 log 和报错信息,同时也可以与当前容器进行互动
可以理解成在 attached 模式下,终端的互动会对接到 docker 的
STDOUT
,STDERR
默认情况下不绑定
STDIN
,但是可以使用-i
flag 去开启 -
后者在 detached 的模式下运行,docker 不会和终端进行互动
使用 -d
这个 flag 可以用来运行 detached 模式:
❯ docker run -p 3030:80 -d ca61c1748
5a4e23de94e7b0a68948fc8c5d7b6f7abbfce7add8848510a91fab95adcaf131
❯ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5a4e23de94e7 ca61c1748 "docker-entrypoint.s…" 3 seconds ago Up 2 seconds 0.0.0.0:3030->80/tcp confident_montalcini
801fe6378b3c ca61c1748 "docker-entrypoint.s…" 38 minutes ago Up 35 minutes 0.0.0.0:3000->80/tcp inspiring_wiles
这个情况下,使用 docker run
也不会 block 终端,如图:
使用 -a
这个 flag 可以运行 attached 模式,如:
❯ docker start -a confident_montalcini
可以看到这里的终端被 block 了
另一种在容器启动后想要继续 attach,则是可以使用 attach
这个指令,如:
❯ docker start confident_montalcini
confident_montalcini
❯ docker attach confident_montalcini
docker ps
logs
docker logs
是用来查看 docker 容器的日志,即负责将容器的 STDOUT
& STDERR
输出到终端,用法如下:
# show logs
❯ docker logs inspiring_wiles
learn docker
learn docker in depth
learn k8s
# show usage
❯ docker logs --help
Usage: docker logs [OPTIONS] CONTAINER
Fetch the logs of a container
Aliases:
docker container logs, docker logs
Options:
--details Show extra details provided to logs
-f, --follow Follow log output
--since string Show logs since timestamp (e.g. "2013-01-02T13:23:37Z") or relative (e.g. "42m" for 42 minutes)
-n, --tail string Number of lines to show from the end of the logs (default "all")
-t, --timestamps Show timestamps
--until string Show logs before a timestamp (e.g. "2013-01-02T13:23:37Z") or relative (e.g. "42m" for 42 minutes)
默认情况下,docker logs
会输出从容器启动后的所有日志,所以也经常会搭配其他的 flag 一起使用,如使用 -f
会进入 attached 模式(只有 STDOUT
& STDERR
),比较适合 debug:
❯ docker logs -f inspiring_wiles
learn docker
learn docker in depth
learn k8s
^C%
效果如下:
一般日常开发中用的比较多的是 -n <number>
去查看最后 <number>
条数据,搭配 -f
一起使用进行 debug,或者直接用时间戳查看 log:
❯ docker logs -f -n 1 inspiring_wiles
learn k8s
^C%
❯ docker logs -t --since 20m inspiring_wiles
2024-04-14T18:22:33.363234022Z learn docker
2024-04-14T18:22:47.814615869Z learn docker in depth
2024-04-14T18:22:51.324651322Z learn k8s
但是在其他环境下,如 tde/qc/uat/prod,遇到问题 aws 会发送警报邮件,在收到警告邮件后使用 --since
搭配 --until
一起去查看固定时间段中发生的问题:
docker logs --since "2023-01-02T12:00:00Z" --until "2023-01-02T14:00:00Z" inspiring_wiles
# 本地没有任何数据,所以输出是空的
这个时间戳会输出 1 月 2 日 12 点-2 点的所有日志,timezone 的话是 UTC。
补充一下,虽然 attached 和 log 绑定的都是 STDOUT
& STDERR
,但是 attached 是可以使用 -i
的 flag 绑定 interactive 模式,但是 log 是只读的
interactive 模式
这个绑定的是 STDIN
案例的话这里用一个 python 脚本去跑好了,我本地也没装 python,用 docker 正好,下面是脚本:
from random import randint
min_number = int(input('Please enter the min number: '))
max_number = int(input('Please enter the max number: '))
if (max_number < min_number):
print('Invalid input - shutting down...')
else:
rnd_number = randint(min_number, max_number)
print(rnd_number)
这是配置的 Dockerfile:
FROM python
WORKDIR /app
COPY . /app
CMD [ "python", "rng.py" ]
但是直接运行的话,会失败:
# 省略掉build
❯ docker run 71d0f063594b4f8
Please enter the min number: Traceback (most recent call last):
File "/app/rng.py", line 3, in <module>
min_number = int(input('Please enter the min number: '))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
EOFError: EOF when reading a line
这是因为 python 脚本预期会从终端中获取用户输入的信息,默认情况下 docker 的运行是 attached 模式,但是STDIN
并不会被绑定,因此会报错。想要开启互动模式就需要使用 -i
,同时经常搭配 -t
去进行 terminal 的授权:
❯ docker run -it 71d0f063594b4f8
Please enter the min number: 0
Please enter the max number: 10
3
重新运行容器,使用 attached 模式并不会报错,不过它也不会从终端读取数据,所以需要用 -a -i
这个 flag 去完整的实现功能:
❯ docker start -ai wizardly_lamarr
Please enter the min number: 10
Please enter the max number: 20
17
删除容器/镜像
跑了一些案例之后我本地也有了一些用不上的容器和镜像,多了也会占用空间,所以也需要定期进行清理,删除的 flag 是 -rm
❯ docker rm wizardly_lamarr sweet_shannon festive_faraday confident_montalcini inspiring_wiles
wizardly_lamarr
sweet_shannon
festive_faraday
confident_montalcini
inspiring_wiles
❯ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ec2f40d48498 mysql "docker-entrypoint.s…" 5 days ago Exited (0) 2 hours ago nice_kirch
对应删除镜像的指令为 rmi
:
❯ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 71d0f063594b 14 minutes ago 1.02GB
<none> <none> ca61c1748170 2 hours ago 1.11GB
mysql latest 374f9fbf70c1 5 days ago 632MB
❯ docker rmi 71d0f063594b ca61c1748170
Deleted: sha256:71d0f063594b4f80bc1e17e0d6631a943584454ff982ba526cdaa4f7368065b1
Deleted: sha256:ca61c17481707811537498b2efd412d03631e461a0d16c4deb4d98c496f3e2b6
❯ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql latest 374f9fbf70c1 5 days ago 632MB
只有在 没有 容器(包括已经终止的容器)使用镜像的时候,当前镜像才能被删除
自动删除已经停止的容器
其实准确的说应该是在容器被停止时,自动删除自己。使用方法是在运行 run 的时候,添加 --rm
flag:
❯ docker run -p 3030:80 -d --rm ca61c1748
92faae4c200c55ec680818dd492838fc318abb3cf1bbfe6bcd0496ddb066fdca
❯ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
92faae4c200c ca61c1748 "docker-entrypoint.s…" 4 seconds ago Up 3 seconds 0.0.0.0:3030->80/tcp naughty_thompson
❯ docker stop naughty_thompson
naughty_thompson
❯ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ec2f40d48498 mysql "docker-entrypoint.s…" 5 days ago Exited (0) 2 hours ago nice_kirch
本地容器互相传输文件
之前碰到的一个案例:[spring] Spring Boot REST API - 项目实现,这个里面我把本地的 sql 文件传到了容器里去运行。用的指令都是 docker cp
,接受的参数是 <filename>
和 <container_name>:<filename>
,文件名后跟 /
代表的是文件夹。
不过需要注意的是,docker 好像不支持多个文件的复制,之前尝试使用 *.sql
失败了,后来研究了一下 bash script 完成了 cv 的过程……现在想想直接 cv 文件夹应该也可以……
从本地向容器传文件的指令如下:
❯ docker cp rng.py ecstatic_kepler:/py_script
Successfully copied 2.05kB to ecstatic_kepler:/py_script
❯ docker exec -it ecstatic_kepler bash
root@786e02116e1e:/app# cd ..
root@786e02116e1e:/# cat py_script
from random import randint
min_number = int(input('Please enter the min number: '))
max_number = int(input('Please enter the max number: '))
if (max_number < min_number):
print('Invalid input - shutting down...')
else:
rnd_number = randint(min_number, max_number)
print(rnd_number)
root@786e02116e1e:/#
从容器向本地 cv 文件:
# copy file from container to local
❯ docker cp ecstatic_kepler:/py_script new_py_script.py
Successfully copied 2.05kB to /Users/luhan/study/docker/py-script/new_py_script.py
❯ cat new_py_script.py
from random import randint
min_number = int(input('Please enter the min number: '))
max_number = int(input('Please enter the max number: '))
if (max_number < min_number):
print('Invalid input - shutting down...')
else:
rnd_number = randint(min_number, max_number)
print(rnd_number)
使用场景如下:
- 需要从本地 cv 一些配置文件到容器里,就像上面的案例一样
- 需要从容器里面获取一些 log,这也是另一个使用场景
命名与版本
开启容器是,使用 --name
即可:
❯ docker run -p 3030:80 -d --rm --name node-app ca61c1748
d83e45b95eaf1e01197b2cd75f56c4fcc0dfd985badab2799baece79cc2a2409
❯ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d83e45b95eaf ca61c1748 "docker-entrypoint.s…" 3 seconds ago Up 2 seconds 0.0.0.0:3030->80/tcp node-app
镜像稍微复杂一些,它的命名规范为 <name>:<tag>
,如:
❯ docker build -t node-app:latest .
# 省略其他 build 过程
=> => naming to docker.io/library/node-app:latest 0.0s
❯ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
node-app latest ca61c1748170 3 hours ago 1.11GB
node latest 5212d7dd5bd4 3 days ago 1.1GB
mysql latest 374f9fbf70c1 5 days ago 632MB
# run docker image to create a container
❯ docker run -p 3030:80 -d --rm --name node-app node-app:latest
f4354ad44f955b0ff4f937e5973d1d2bc5eb5c35eb6ead440fae5307253d9416