Docker使用

Docker 安装

下载: https://docs.docker.com/get-docker/

Win 10(家庭版)

需要安装Hyper-V才可以。将如下代码添加到记事本中,并另存为 Hyper-V.cmd 文件。右键以管理员身份运行,然后等待安装,最后输入 Y 进行重启即可

pushd "%~dp0"
dir /b %SystemRoot%\servicing\Packages\*Hyper-V*.mum >hyper-v.txt
for /f %%i in ('findstr /i .hyper-v.txt 2^>nul') do dism /online /norestart /add-package:"%SystemRoot%\servicing\Packages\%%i"
del hyper-v.txt
Dism /online /enable-feature /featurename:Microsoft-Hyper-V-All /LimitAccess /ALL

重启完后双击 docker 安装包安装,再次重启,启动 docker,可能提示 wsl 版本低,wsl --update

Win 10 (专业版)

启用Hyper-V进行安装:在控制面板—启用或关闭 Windows 功能,勾选Hyper-V容器两个选项,两个都要,不然报错。然后重启,双击 exe 文件。

注意:如果使用安卓虚拟机的话,二者会有冲突。安卓虚拟机需要关闭 Hyper-V,docker 需要开启 Hyper-V。有文章说在控制面板—启用或关闭 Windows 功能—启用 Windows 虚拟机监控程序平台,勾选这个可以解决冲突,但我试了没有用。我用夜神模拟器有冲突,改用蓝叠 4 模拟器(hyper-v)版本

配置阿里云镜像: https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors

修改默认位置:默认是在 C 盘

打开设置,选择存储位置

之前眼瞎没找见设置里的 Disk image location 选项,找了另一篇文章里的方法。

C:\Users\> wsl --list -v  # 退出docker,用任务管理器杀进程
  NAME                   STATE           VERSION
* docker-desktop         Stopped         2
  docker-desktop-data    Stopped         2

C:\Users\> wsl --export docker-desktop-data "D:\\docker-desktop-data.tar" # 将docker-desktop-data导出到文件中(备份image及相关文件)
正在导出,这可能需要几分钟时间。   
操作成功完成。 

C:\Users\> wsl --unregister docker-desktop-data # 从wsl取消注册docker-desktop-data
正在注销。
操作成功完成。 

C:\Users\> wsl --import docker-desktop-data "E:\\DockerImage\\wsl" "D:\\docker-desktop-data.tar" --version 2 # 将导出的docker-desktop-data再导入回wsl,并设置我们想要的路径,即新的镜像及各种docker使用的文件的挂载目录,我这里设置到E:\\DockerImage\\wsl

参考: https://blog.csdn.net/u013948858/article/details/111464534

Debian 10

安装 Docker

1)直接使用脚本安装:

curl -sSL https://get.docker.com/ | sh
# 使用阿里镜像源
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun

2)执行以下步骤从 Docker 的存储库安装最新的稳定 Docker 版本。root 用户下执行

apt update
# 安装通过 HTTPS 添加新存储库所需的软件包:
apt install apt-transport-https ca-certificates curl software-properties-common gnupg2
# 使用以下 curl 命令导入存储库的 GPG 密钥;成功后,命令将返回 OK 。
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
# 将稳定的 Docker APT 存储库添加到系统的软件存储库列表中:
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"
# 更新 apt 软件包列表并安装最新版本的 Docker CE (Community Edition) 
apt update
apt install docker-ce

使用阿里云存储库(推荐)

参考: https://help.aliyun.com/zh/ecs/use-cases/install-and-use-docker

这是 Debian 系统的命令,其他系统请查看文档

#更新包管理工具
sudo apt-get update
#添加Docker软件包源
sudo apt-get -y install apt-transport-https ca-certificates curl software-properties-common
sudo curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/debian/gpg | sudo apt-key add -
sudo add-apt-repository -y "deb [arch=$(dpkg --print-architecture)] https://mirrors.aliyun.com/docker-ce/linux/debian $(lsb_release -cs) stable"
#安装Docker社区版本,容器运行时containerd.io,以及Docker构建和Compose插件
sudo apt-get -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
#启动Docker
sudo systemctl start docker
#设置Docker守护进程在系统启动时自动启动
sudo systemctl enable docker

3)离线安装

因网络问题,导致 docker 无法联网安装,所以使用离线安装的方式。直接参考这个文章: https://blog.csdn.net/weixin_42571882/article/details/134015815

下载 docker 安装包: https://download.docker.com/linux/static/stable/x86_64/ ,网盘下载:docker-25.0.5.tgz

# 注意从网盘下载的文件后缀名为docker-25.0.5.tgz.zip,需要先改为docker-25.0.5.tgz
mv docker-25.0.5.tgz.zip docker-25.0.5.tgz  # 改后缀
tar -zxvf docker-25.0.5.tgz  # 解压docker安装包
cp docker/* /usr/bin/        # 将解压后的文件复制到/usr/bin/目录下

赋予文件执行权限

# 赋予文件执行权限,可以先赋予,在复制到/usr/bin/目录下
chmod +x /usr/bin/docker*
查看是否有执行权限
ls -l /usr/bin/docker*

将 docker 注册成系统服务

vim /etc/systemd/system/docker.service

添加如下内容,然后保存退出(我也不知道这些内容的具体含义,反正目前用着没问题)

[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target firewalld.service
Wants=network-online.target

[Service]
Type=notify
ExecStart=/usr/bin/dockerd
ExecReload=/bin/kill -s HUP $MAINPID
LimitNOFILE=infinity
LimitNPROC=infinity
TimeoutStartSec=0
Delegate=yes
KillMode=process
Restart=on-failure
StartLimitBurst=3
StartLimitInterval=60s

[Install]
WantedBy=multi-user.target

赋予文件执行权限

chmod +x /etc/systemd/system/docker.service
systemctl daemon-reload
systemctl start docker
# 安装命令合集
wget http://oss.lanhuli.top/docker_files/docker-23.0.6.tgz && tar -xvf docker-23.0.6.tgz && chmod 755 ./docker/* && cp ./docker/* /usr/bin/ && wget http://oss.lanhuli.top/docker_files/docker-compose-2.17.0 && chmod 755 docker-compose-2.17.0 && mv docker-compose-2.17.0 /usr/local/bin/docker-compose && rm -rf docker && rm -rf docker-23.0.6.tgz

# 注册systemctl服务同上
查看安装的docker版本
apt list -a docker-ce
apt-cache madison docker-ce | awk '{ print $3 }'  # 结果同上

修改 docker 的存储路径

方法一:使用软连接(个人常用)

# 查看现在的存储路径,默认是 /var/lib/docker
docker info | grep "Root Dir"
# 停止docker服务
systemctl stop docker
# 将这个目录复制到/opt目录下,名字任意
cp -r /var/lib/docker /opt/docker_images
# 删除原来的目录
rm -r /var/lib/docker
# 创建软链接
ln -s /opt/docker_images /var/lib/docker
# 记得重启docker或系统
systemctl start docker

方法二:修改配置文件

因为我这里没有 /etc/docker/daemon.json 这个文件,遂放弃。

安装 docker-compose

# 从github下载docker-compose二进制文件,这可能需要一段时间
curl -L "https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# 给予运行权限
chmod +x /usr/local/bin/docker-compose
# 查看安装版本
docker-compose -v

Docker 命令

参考:Docker 命令大全|菜鸟教程

批量处理命令

docker ps -a  # 查看所有容器
docker ps -a -q  # 查看所有容器ID

docker stop $(docker ps -a -q)  # stop停止所有容器
docker  rm $(docker ps -a -q)  # remove删除所有容器

镜像仓库

login/logout

docker login -u 用户名 -p 密码   # 登陆到一个Docker镜像仓库,如果未指定镜像仓库地址,默认为官方仓库 Docker Hub
docker logout  # 登出Docker Hub

参数说明:

NAME: 镜像仓库源的名称

DESCRIPTION: 镜像的描述

OFFICIAL: 是否 docker 官方发布

stars: 类似 Github 里面的 star,表示点赞、喜欢的意思。

AUTOMATED: 自动构建。

--automated :只列出 automated build类型的镜像;  
--no-trunc :显示完整的镜像DESCRIPTION(描述),不省略;  
-f <过滤条件>:列出收藏数(点赞)不小于指定值的镜像。  
--limit 5:列出前5个镜像

build

docker build:用于使用 Dockerfile 创建镜像

--tag,-t:镜像的名字及标签

-f:指定要使用的 Dockerfile 路径

docker build -t bluelotus_xss_docker:2.0 .
docker build -f /path/to/a/Dockerfile .

注意:最后要加路径,这里最后有个空格和点,代表上下文路径。

说明:构建会在Docker后台守护进程(daemon)中执行,而不是 CLI 中。构建前,构建进程会将全部内容(递归)发送到守护进程。大多情况下,应该将一个空目录作为构建上下文环境,并将 Dockerfile 文件放在该目录下。

pull

语法:docker pull [OPTIONS] NAME[:TAG|@DIGEST]

OPTIONS 选项:

–all-tags, -a: 用于下载该存储库中具有不同标签的所有镜像。

–disable-content-trust: 它会在拉取之前跳过镜像验证。

–platform: 用于设置平台。

–quiet, -q: 用于静默拉取镜像(不冗长)。

–help: 了解更多有关命令的信息

docker pull citizenstig/dvwa  # 默认tag是:latest,更多tag需要再hub中查看
docker pull mysql:5.7
docker pull centos:centos6.8

push

注意:发布镜像时,需要将 image 重新命名,命名格式应为: dockerhub 用户名/镜像名

docker tag bluelotus_xss_docker:1.0 bluefoxqaq/bluelotus_xss_docker:1.0
docker login -u admin -p 123456
docker push bluefoxqaq/bluelotus_xss_docker:1.0

本地镜像管理

images

docker images:列出本地镜像。

语法:docker images [OPTIONS] [REPOSITORY[: TAG]]

OPTIONS 说明:

  • -a : 列出本地所有的镜像(含中间映像层,默认情况下,过滤掉中间映像层);
  • --digests : 显示镜像的摘要信息;
  • -f : 显示满足条件的镜像;
  • --format : 指定返回值的模板文件;
  • --no-trunc : 显示完整的镜像信息;
  • -q : 只显示镜像 ID。

rmi

docker rmi : 删除本地一个或多个镜像。

语法:docker rmi [OPTIONS] IMAGE [IMAGE...]

OPTIONS 说明:

  • -f : 强制删除
  • --no-prune : 不移除该镜像的过程镜像,默认移除

例如:docker rmi -f runoob/ubuntu:v4

tag

docker tag:为本地镜像添加一个新的标签

docker tag bluelotus_xss_docker:1.0  bluefoxqaq/bluelotus_xss_docker:1.0

注意:它们是同一个镜像,只是别名不同而已。

inspect

docker inspect:获取镜像的详细信息,其中,包括创建者,各层的数字摘要等。

-f ,--format:指定返回值的模板文件。Go 模板格式化输出

docker inspect debian:12.5

# 查看镜像的操作系统架构(arm还是x86)
docker inspect --format='{{.Os}}/{{.Architecture}}' e6bce4a855e5
# 获取正在运行的容器dvwa的 IP
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' dvwa

save

docker save:将指定镜像保存成 tar 归档文件。

-o :输出到的文件。

# 例如
docker save -o my_ubuntu_v3.tar runoob/ubuntu:v3
docker save -o xss_platfrom_docker.tar  79bb31b88efb

load

docker load:导入使用 docker save 命令导出的镜像。

-i:指定导入的文件

-q:精简输出信息

docker load -i xss_platfrom_docker.tar

export 和 import 导出的是一个容器的快照, 不是镜像本身, 也就是说没有 layer。你的 dockerfile 里的 workdir, entrypoint 之类的所有东西都会丢失,commit 过的话也会丢失。快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也更大。

docker save 保存的是镜像(image),docker export 保存的是容器(container);docker load 用来载入镜像包,docker import 用来载入容器包,但两者都会恢复为镜像;docker load 不能对载入的镜像重命名,而 docker import 可以为镜像指定新名称。

容器操作

ps

docker ps : 列出容器

  • -a : 显示所有的容器,包括未运行的。
  • -f : 根据条件过滤显示的内容。
  • --format : 指定返回值的模板文件。
  • -l : 显示最近创建的容器。
  • -n : 列出最近创建的 n 个容器。
  • --no-trunc : 不截断输出。
  • -q : 静默模式,只显示容器编号。
  • -s : 显示总的文件大小。

常用示例

docker ps -n 3  # 显示最近创建的 3 个容器,包括所有状态
docker ps -s    # 显示容器的大小

# 格式化输出
docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Status}}"
docker ps -s --format "table {{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Size}}"
docker ps -a -s --format "table {{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Size}}"
docker ps -a -s --format "table {{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Size}}\t{{.Ports}}"

可用的

参考: https://www.runoob.com/docker/docker-ps-command.html

run

docker run : 创建一个新的容器并运行一个命令

  • -a stdin: 指定标准输入输出内容类型,可选 STDIN/STDOUT/STDERR 三项;
  • -d: 后台运行容器,并返回容器 ID;
  • -i: 以交互模式运行容器,通常与 -t 同时使用;
  • -P: 随机端口映射,容器内部端口随机映射到主机的端口
  • -p: 指定端口映射,格式为:主机 (宿主) 端口: 容器端口
  • -t: 为容器重新分配一个伪输入终端,通常与 -i 同时使用;
  • --name="nginx-lb": 为容器指定一个名称;
  • --dns 8.8.8.8: 指定容器使用的 DNS 服务器,默认和宿主一致;
  • --dns-search example. com: 指定容器 DNS 搜索域名,默认和宿主一致;
  • -h "mars": 指定容器的 hostname;
  • -e username="ritchie": 设置环境变量;
  • --env-file=[]: 从指定文件读入环境变量;
  • --cpuset="0-2" or --cpuset="0,1,2": 绑定容器到指定 CPU 运行;
  • -m : 设置容器使用内存最大值;
  • --network="bridge": 指定容器的网络连接类型,支持 bridge/host/none/container: 四种类型;
  • **--link=[]: 添加链接到另一个容器;
  • --expose=[]: 开放一个端口或一组端口;
  • --volume , -v: 绑定一个卷

start/stop/restart

docker start:启动一个或多个已经被停止的容器(docker start container1 container2 container3)

docker stop:停止一个运行中的容器(docker stop container1 container2 container3)

docker restart:重启容器

exec

docker exec :在运行的容器中执行命令

  • -d : 分离模式: 在后台运行
  • -i : 即使没有附加也保持 STDIN 打开
  • -t : 分配一个伪终端
  • -u,--user:指定用户

例如:

$ docker exec -i -t  mynginx /bin/bash  # 在容器 mynginx 中开启一个交互模式的终端
$ docker exec -it 9df70f9a0714 /bin/bash  # 9df70f9a0714 是容器 id
$ docker exec 9df70f9a0714 service nginx strart
$ docker exec -u www-data -it nextcloud ls  # 用www-data用户执行命令

cp

docker cp : 用于容器与主机之间的数据拷贝。

语法:docker cp [OPTIONS] srcPath destPath

例如:

将主机/ www/runoob目录拷贝到容器 96f7f14e99ab 的/ www目录下 。

docker cp /www/runoob 96f7f14e99ab:/www/

将主机/ www/runoob目录拷贝到容器 96f7f14e99ab 中,目录重命名为 www

docker cp /www/runoob 96f7f14e99ab:/www

将容器 96f7f14e99ab 的/ www目录拷贝到主机的/tmp目录中 。

docker cp  96f7f14e99ab:/www /tmp/

rm

docker rm:删除一个或多个容器。

强制删除容器 db01db02docker rm -f db01 db02

update

docker update:更新一个或多个容器的配置

docker update --restart=always 容器 ID (或者容器名)  # 容器开机自启

rename

将容器重命名

docker rename 原容器名称 新容器名称

logs

docker logs : 获取容器的日志

--since:显示某个开始时间的所有日志

-t:显示时间戳

--tail:仅列出最新 N 条容器日志

# 查看某时间之后最新的 100 条日志:
docker logs -t --since="2021-08-18T 11:46:37" --tail=100 CONTAINER_ID)

stats

docker stats:查看容器资源的使用情况,包括:CPU、内存、网络 I/O 等

  • --all , -a:显示所有的容器,包括未运行的。
  • --format:指定返回值的模板文件。
  • --no-stream:展示当前状态就直接退出了,不再实时更新。
  • --no-trunc:不截断输出。

  • CONTAINER ID 与 NAME:容器 ID 与名称。
  • CPU % 与 MEM %:容器使用的 CPU 和内存的百分比。
  • MEM USAGE / LIMIT:容器正在使用的总内存,以及允许使用的内存总量。
  • NET I/O:容器通过其网络接口发送和接收的数据量。
  • BLOCK I/O:容器从主机上的块设备读取和写入的数据量。
  • PIDs:容器创建的进程或线程数。

网络管理

查看容器 ip 地址:docker inspect 容器id

docker network create:创建一个 docker 局域网络

docker network create mynetwork  # 随机网段
docker network create --subnet 172.16.7.0/24 mynetwork # 指定网段

docker network connnet:将某个容器连接到一个 docker 网络

docker network connect mynetwork nginx
docker network connect mynetwork 容器id
# 在 docker run 时配置网络信息
docker run --name mynginx --network mynetwork --network-alias nginx --ip 172.16.7.2  镜像id
# --network :加入 docker 内部网络
# --network-alias :配置容器在内部局域网的网络别名
# --ip:配置在内部局域网的 ip 地址  172.172.0.1 是网关
# 一个名为 mynginx 的容器,加入到 mynetwork 的网络当中,他的网络名为 nginx。
# 启动 Docker 容器的时候,使用默认的网络是不支持指派固定 IP 的

docker network disconnect:将某个容器退出某个局域网络

docker network ls:显示所有 docker 局域网络

docker network inspect:显示某个局域网络信息

docker network rm:删除 docker 网络

docker network prune:删除所有未引用的 docker 局域网络

数据卷容器管理

docker volume create <volume_name>    # 创建数据卷
docker run -v <volume_name>:/home/data nginx  # 将数据卷挂载到容器
docker volume ls         # 查看数据卷  
docker volume rm         # 删除数据卷  
docker volume inspect    # 查看数据卷详情  
docker volume prune      # 删除未使用的数据卷
  • 数据卷不存在时会自动创建
  • 数据卷中没有文件时,会将容器目录中的文件挂载到数据卷中,数据卷中有文件时,会将数据卷中的文件覆盖容器目录中的文件

Docker-Compose

Docker-Compose 项目是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排。

安装

# 从 github 下载 docker-compose 二进制文件,这可能需要一段时间
curl -SL https://ghproxy.com/https://github.com/docker/compose/releases/download/v2.15.1/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose

# 给予运行权限
chmod +x /usr/local/bin/docker-compose
# 查看安装版本
docker-compose -v

简单使用

docker-compose up :该命令可以自动完成包括构建镜像,(重新) 创建服务,启动服务,并关联服务相关容器的一系列操作。 默认情况下,docker-compose up 启动的容器都在前台,控制台将会同时打印所有容器的输出信息,可以很方便进行调试。当通过 Ctrl+c 停止命令时,所有容器将会停止。如果希望在后台启动并运行所有的容器,使用 docker-compose up -d

docker-compose ps :列出项目中目前的所有容器

docker-compose pull :拉取服务依赖的镜像

docker-compose restart :重启项目中的服务

docker-compose start :启动已经存在的服务容器。

docker-compose stop :停止正在运行的容器,可以通过 docker-compose start 再次启动

docker-compose rm :删除所有(停止状态的)服务容器。选项包括: –f, –force,强制直接删除,包括非停止状态的容器 -v,删除容器所挂载的数据卷

注意:stop 是只停掉容器不删除;down 是停掉容器然后删除掉二者区别

docker-compose logs -f

yaml 文件编写说明

version: '3'
services:
  webdav:
    image: bytemark/webdav
    restart: always
    ports:
      - "8880:80"
    environment:
      AUTH_TYPE: Digest
      USERNAME: xujiajun
      PASSWORD: xujiajun
    volumes:
      - /srv/dav:/var/lib/dav

docker run、create、start 命令区别

参考文章:docker中run、start和create命令的区别_allway2的博客-CSDN博客

docker create:从 Docker 映像创建一个全新的容器。但是,它不会立即运行它。

docker start:将启动任何已停止的容器。如果使用 docker create 命令创建容器,则可以使用此命令启动它。

docker run :是创建和启动的组合,因为它创建了一个新容器并立即启动它。实际上,如果 docker run 命令在您的系统上找不到上述映像,它甚至可以从 Docker Hub 中提取映像。docker run 只在第一次运行时使用

Docker 网络使用

Docker 容器运行的时候有 host、bridge、none 三种网络可供配置。默认是 bridge,即桥接网络,以桥接模式连接到宿主机;host 是宿主网络,即与宿主机共用网络;none 则表示无网络,容器将无法联网。

容器网络别名:容器在网络是是允许有别名的,且这个别名在所在网络中都可以直接访问,这就类似局域网在各物理机的主机名。

注意:必须创建一个自定义的网络,在这个网络里面,才可以使用别名

为什么默认的网卡不可以使用别名进行 IP 地址解析呢用户自定义的网卡可以在容器之间提供自动的 DNS 解析,缺省的桥接网络上的容器只能通过 IP 地址互相访问,除非使用 --link 参数。在用户自定义的网卡上,容器直接可以通过名称或者别名相互解析。

docker 的 puid 和 pgid

参考: https://www.cnblogs.com/sparkdev/p/9614164.html

容器内的 root 用户和宿主机上的 root 用户是同一个。也就是说,docker 容器和宿主机内核共用一套 uid 和 gid 进行控制。

在 docker compose 的 yml 文件中指定 puid 和 pgid 为 1000,当这个容器创建运行的时候,进入容器里面。可以看到文件的所有者是用户 abc,他的 puid 值为 1000。

退出容器返回宿主机,查看 passwd 文件,查找 uid 为 1000 的用户。可以看到用户名为 share 123。使用 id 命令进行查看,它的 uid 为 1000,gid 为 100,表示当前工作组是 users, 其中 share 123 既是 users 组,又是 omv-share 组。

Dockerfile

Dockerfile 的指令是从上到下依次执行。其实跟我们直接敲命令差不多。

# 一些常见指令
FROM:指定基础镜像,必须
WORKDIR :设置指令的工作目录。不存在就创建。容器启动后,使用docker exec命令默认进入到这个目录下
COPY :将文件或目录复制到镜像中,第一个参数是宿主机文件,第二个是容器
RUN :镜像创建阶段中执行的命令
CMD :容器创建阶段中执行的命令,可以被覆盖。例如在 docker run 镜像 id xxxx 时
EXPOSE :声明端口号
# EXPOSE 只是声明,起到一个文档提示的作用,在运行 docker run 时可以用-p 参数指定端口映射,或者使用-P 参数,docker 会根据 EXPOSE 自动映射随机端口

RUN 和 CMD 的区别:RUN 是在创建镜像时执行的命令,一般是安装、下载、创建文件之类的。CMD 是在创建容器时才会执行的命令,一般是启动程序的命令。

CMD 两种写法

exec 模式:不会通过 shell 执行相关的命令,所以向 $HOME 这样的环境变量是获取不到的。

格式:CMD ["可执行文件","参数 1","参数 2"]

shell 模式:docker 以 /bin/sh -c command 的方式执行命令,也就是说容器的 1 号进程是 bash 进程。

格式:CMD command

例如:

CMD ["/bin/echo","hello"],会输出 hello。

CMD echo hello,也会输出 hello,但是他其实是这样的 CMD ["","-c","echo hello"]

CMD echo $HOME 实际执行是 CMD [ "sh", "-c", "echo $HOME" ]

推荐使用 exec 模式,如果命令太多可以写成一个脚本,然后运行这个脚本。

一个简单的 Dockerfile:安装 Nginx 并启动

# 需要一个 Debian 9.4 的镜像
FROM debian: 9.4  

# 上传 apt 源配置
COPY ./sources.list /etc/apt/sources.list
# 安装 Nginx
RUN apt update ; apt install nginx -y
# 声明端口,仅仅只是一个声明,描述需要哪些端口
EXPOSE 80

#启动Nginx
COPY start_nginx.sh /root/start_nginx.sh
WORKDIR /root/
RUN chmod 777 /root/start_nginx.sh
CMD ["sh","-c","./start_nginx.sh"]

FROM debian: 9.4:指定一个基础镜像,需要一个 Debian 9.4 的镜像

COPY ./sources.list /etc/apt/sources.list:将宿主机的 ./sources.list 复制到容器的 /etc/apt/sources.list,这个 sources.list 文件,是提前准备好的 apt 源。

RUN apt update ; apt install nginx -y:执行两条命令,更新源和安装 Nginx,这里用了分隔符执行两个命令,也可以分开写,用两个 RUN。

COPY start_nginx.sh /root/start_nginx.sh:将 Nginx 启动脚本复制到容器。脚本内容在后面说。

WORKDIR /root/:来到容器的 /root/ 目录下,也就是存放 Nginx 启动脚本的目录。

RUN chmod 777 /root/start_nginx.sh:赋予 Nginx 启动脚本执行权限。

CMD ["sh","-c","./start_nginx.sh"]:运行脚本。

先看看启动脚本内容

#!/bin/sh
/usr/sbin/service nginx start
/bin/bash

这里需要了解一下 docker 的机制:

Docker 不是虚拟机,容器中的应用都应该以前台执行,而不是像虚拟机、物理机里面那样,用 systemd 去启动后台服务,容器内没有后台服务的概念。

docker 容器运行必须有一个前台进程,如果没有前台进程执行,容器认为空闲,就会自行退出。一般常见的启动参数是 docker run -dit centos /bin/bash/bin/bash 就是一个前台进程,你也可以用 top 之类的命令。

使用 CMD service nginx start 命令作为容器启动命令的话,这个命令执行成功后就会退出,紧接着容器也会自动退出。对于容器而言,其启动程序就是容器应用进程,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,从而退出,其它辅助进程不是它需要关心的东西。

使用 service nginx start 命令,则是希望 upstart 来以后台守护进程形式启动 nginx 服务。而 CMD service nginx start 会被理解为 CMD [ "sh", "-c", "service nginx start"],因此主进程实际上是 sh。那么当 service nginx start 命令结束后,sh 也就结束了,sh 作为主进程退出了,自然就会令容器退出。

所以在启动脚本当中最后是 /bin/bash 命令。防止容器退出。也可以将 nginx 放到前台运行 CMD ["nginx", "-g", "daemon off;"]

注意:docker run xxxxxxxxxxxxx /bin/bash ,这里的/bin/bash 会覆盖掉 CMD 的命令。

查看容器大小

docker ps -s --format "table {{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Size}}"
# -s参数,显示容器大小
# --format "table {{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Size}}",格式化输出

SIZE 列,就是容器占用空间的大小,那这个括号外面的和里面的分别是什么意思

  • 括号外面的,如:199MB. 表示现在向容器的可写层写入的数据量的大小。
  • 括号里面的(virtual 313MB)。表示:镜像大小 + 可写层数据量大小之和。

容器大小 = 括号外面的 + 括号里面的

例如 python-oss 容器的大小为:199+313=512MB