以下操作均在 root 用户下进行,否则请带上
sudo
,或者是把用户加到 docker 用户组内。
以基于 Python 的服务为例(未考虑安装第三方模块的情况):
docker run -dit \
--name my_proj \
-v 源码和数据目录:/usr/src/myapp \
-w /usr/src/myapp \ # 设置工作目录
python:3 \
python 启动脚本
第一层:构建时把源码放进去,数据仍然放在外面:
第二层:构建时把源码放进去,数据也放进去,或用数据库连接等方式获取数据:
实际情况一般会放在镜像库,通过镜像库拉取;也可通过传文件的方式手动分发:
其有 Dockerfile
,构建后为一个服务器。
# Web demo of myenigma
# https://github.com/DingJunyao/myenigma.git
# ./Dockerfile
FROM python:3.10 # 从 `python:3.10` 拉取镜像
WORKDIR /app # 设置容器内工作目录为 `/app`
COPY . /app # 将宿主机当前目录放在容器内 `/app` 下
RUN pip install -r requirements.txt \ # 根据 `requirements.txt` 在容器内安装 Python 模块
-i \
http://mirrors.fsc.efoxconn.com/pypi/simple/ \
--trusted-host mirrors.fsc.efoxconn.com # 在公司内网需要换源
EXPOSE 8080 # 暴露容器内的 `8080` 端口
ENV PYTHONPATH "${PYTHONPATH}:/app" # 将容器内应用目录添加到容器内 `PYTHONPATH` 环境变量
CMD ["python", "web_demo/web_demo.py"] # 设置启动容器时的命令
进入构建脚本所在目录,执行:
docker build -t 镜像名[:镜像标签名] .
如果构建脚本非当前目录下的 Dockerfile
,则添加路径:
docker build -t 镜像名[:镜像标签名] -f 构建脚本路径 .
若上下文路径非当前目录,则添加路径:
docker build -t 镜像名[:镜像标签名] -f 构建脚本路径 上下文路径
最后在本地生成对应的镜像,可使用它运行为容器。
使用 Docker Compose,可以一键构建、部署。
下文的“默认情况”指:
Dockerfile
,在上下文目录根目录image
参数可不填,如不填则会生成一个镜像名。
需添加 build
参数:
.
即可version: "3.9"
services:
myenigma_srv:
image: myenigma_img
build: .
container_name: myenigma
ports:
- 10000:8080
context
中填上下文路径 build:
context: 上下文路径
dockerfile
参数中 build:
context: 上下文路径
dockerfile: 构建脚本路径
默认情况下,名称为 Dockerfile
。
最好新建一个目录来准备构建镜像的工作(其路径被称为上下文路径)。
要把所有要放到镜像中的文件或目录放到里面,Dockerfile
最好也放在里面。
文件越少越好,以加快速度。
Docker 为 C-S 模式,上下文路径由 Docker CLI 打包传输给 Docker 守护进程。
每个指令都是一层镜像。
过多无意义的层,会造成镜像膨胀过大。
root@ding-server:~# docker image history myenigma:latest
IMAGE CREATED CREATED BY SIZE COMMENT
2460049ba1c0 25 seconds ago /bin/sh -c #(nop) CMD ["python" "web_demo/w… 0B
b32be1a97716 28 seconds ago /bin/sh -c #(nop) ENV PYTHONPATH=:/app 0B
37aaba26044c 30 seconds ago /bin/sh -c #(nop) EXPOSE 8080 0B
def7573ab3f8 38 seconds ago /bin/sh -c pip install -r requirements.txt -… 60.7MB
3a9a5a111d71 About a minute ago /bin/sh -c #(nop) COPY dir:4ae6d4478d9aaaf38… 49.1kB
06046167b0d5 About a minute ago /bin/sh -c #(nop) WORKDIR /app 0B
6bb8bdb609b6 6 days ago /bin/sh -c #(nop) CMD ["python3"] 0B
<missing> 6 days ago /bin/sh -c set -eux; wget -O get-pip.py "$… 10.2MB
<missing> 6 days ago /bin/sh -c #(nop) ENV PYTHON_GET_PIP_SHA256… 0B
<missing> 6 days ago /bin/sh -c #(nop) ENV PYTHON_GET_PIP_URL=ht… 0B
<missing> 6 days ago /bin/sh -c #(nop) ENV PYTHON_SETUPTOOLS_VER… 0B
<missing> 6 days ago /bin/sh -c #(nop) ENV PYTHON_PIP_VERSION=22… 0B
<missing> 6 days ago /bin/sh -c set -eux; for src in idle3 pydoc… 32B
<missing> 6 days ago /bin/sh -c set -eux; wget -O python.tar.xz… 56.8MB
<missing> 6 days ago /bin/sh -c #(nop) ENV PYTHON_VERSION=3.10.5 0B
<missing> 2 weeks ago /bin/sh -c #(nop) ENV GPG_KEY=A035C8C19219B… 0B
<missing> 2 weeks ago /bin/sh -c set -eux; apt-get update; apt-g… 18.5MB
<missing> 2 weeks ago /bin/sh -c #(nop) ENV LANG=C.UTF-8 0B
<missing> 2 weeks ago /bin/sh -c #(nop) ENV PATH=/usr/local/bin:/… 0B
<missing> 2 weeks ago /bin/sh -c set -ex; apt-get update; apt-ge… 529MB
<missing> 2 weeks ago /bin/sh -c apt-get update && apt-get install… 152MB
<missing> 2 weeks ago /bin/sh -c set -ex; if ! command -v gpg > /… 19MB
<missing> 2 weeks ago /bin/sh -c set -eux; apt-get update; apt-g… 10.7MB
<missing> 2 weeks ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 2 weeks ago /bin/sh -c #(nop) ADD file:dd3d4b31d7f1d4062… 124MB
FROM
- 以哪个镜像为基础绝大多数镜像以 ubuntu
、debian
或 alpine
为基础;而这些基础来源于空“镜像”scratch
。
一般会以已经搭建好环境的镜像为基础。
一个轻量化的 Linux 发行版。
Shell、内部命令和基础的外部命令由 busybox 提供,一些 bash 中的功能可能不支持(如 echo -e
)。
包管理器是 apk
。
通过各自的包管理器安装 MySQL 客户端后,打出的包的大小对比:
alpine
:36.8 MBubuntu
:145 MB故经常用于 Docker 镜像的构建。但缺乏一些基础的库,构建镜像时可能需要额外安装。
COPY
:
COPY [--chown=用户名[:用户组名] 源路径1 [源路径2 ...] 目标路径
COPY [--chown=用户名[:用户组名]] ["源路径1", ["源路径2", ...,] "目标路径"]
# 第二条指令中,包裹全部路径的方括号要写上
如目录不存在会自动创建。
ADD
格式与 COPY
类似,但源文件扩展名为 tar
、gz
、bz2
、xz
时,会自动解包、解压缩到目标路径。
建议优先使用 COPY
。
RUN
- 构建时在容器内执行命令实质上是使用 /bin/sh -c
执行命令。
RUN 命令 参数1 参数2 ...
RUN ["命令", "参数1", "参数2"...]
运行时,能写成一条语句则写成一条(可用 \
换行),以减少层数。
涉及管道的命令,直接按上面方法写,前面出错后面仍然会执行,应改为:
RUN set -o pipefail && 命令 参数1 参数2 ... | 命令2 ...
如果是 Debian,默认的 sh
用的是 dash
,需用 bash
执行:
RUN ["/bin/bash", "-c", "set -o pipefail && 命令1 参数1 参数2 ... | 命令2 ..."]
apt
可以使用 sed
替换 /etc/apt/sources.list
中的软件源地址,或者是直接把该文件替换为已经改好的。
关于
sed
的用法参见 《Linux 入门 - 11 - 正则表达式与数据处理》 中的 《sed
- 文本的流编辑器》。
使用该语句以更新软件源、安装软件包,最后清除缓存:
RUN apt-get update \
&& apt-get install -y 软件包 \
&& rm -rf /var/lib/apt/lists/*
apk
apk
的软件源列表在 /etc/apk/repositories
,一行一个网址:
http://mirrors.fsc.efoxconn.com:8081/repository/apk-proxy/main
http://mirrors.fsc.efoxconn.com:8081/repository/apk-proxy/community
可以使用 sed
替换软件源地址,或者是直接把该文件替换为已经改好的。
使用该语句以安装软件包:
RUN apk add --no-cache 软件包
ENV
- 设置环境变量ENV 变量名=值
通过这种方式设置的环境变量,无法在后续通过执行 unset
命令删除。如需使其能够在后续被删除,请换用 RUN
执行设置环境变量的命令。
CMD
- 指定默认运行命令一般写在最后;如有多个,以最后一个为准。
docker run
时执行,运行结束则容器结束。
CMD ["命令", "参数1", "参数2"...]
对于服务器等有守护进程的容器来说,填写的一般是启动守护进程的命令(此时守护进程要在前台运行)。
如 docker run
时指定了命令,则以运行时指定的为准。
运行容器时,首次执行的命令即为 PID 为 1
的命令。
ENTRYPOINT
- 指定默认运行命令一般写在最后;如有多个,以最后一个为准。
ENTRYPOINT ["命令", "参数1", "参数2"...]
不会被 docker run
时指定的命令覆盖,反而会追加;故可与 CMD
合用,表示定参和变参:
ENTRYPOINT ["nginx", "-c"] # 定参
CMD ["/etc/nginx/nginx.conf"] # 变参
# 设构建出来的镜像为 nginx_mod
FROM nginx
ENTRYPOINT ["nginx"] # 定参
CMD ["-c", "/etc/nginx/nginx.conf"] # 变参
docker run nginx_mod # nginx -c /etc/nginx/nginx.conf
docker run nginx_mod -c /etc/nginx/new.conf # nginx -c /etc/nginx/new.conf
# 上面的命令里面,/etc/nginx/new.conf 要在容器中有
docker-entrypoint.sh
示例许多 Docker 镜像都会以该文件作为 ENTRYPOINT
。
该文件在各镜像有不同,以 PostgreSQL(postgres
) 的为例:
#!/bin/bash
set -e # 脚本里任何一行命令的退出状态码为非零时,Shell 立即退出
if [ "$1" = 'postgres' ]; then # $0 为 "docker-entrypoint.sh",故看后面的参数
# 意为 若传入的命令的第一节为 postgres
chown -R postgres "$PGDATA" # 变更所有权。$PGDATA 此前已定义,或等待执行命令时重新定义
if [ -z "$(ls -A "$PGDATA")" ]; then # 若无该目录
gosu postgres initdb # gosu 替代 su、sudo,避免 PID、信号问题。之前已安装
# 这里是用 postgres 用户执行 initdb
fi
exec gosu postgres "$@" # exec 命令会用要执行的命令替换 Shell,命令执行完成则终止
# 这里是使用 postgres 用户执行传入的命令(默认是 postgres,即守护进程)
# 使用 exec 和 gosu 的目的,是为了确保执行的命令的 PID 为 1,使其能够对外传递信号
# 如执行完毕或意外退出,能够让 Docker 守护进程知道
fi
exec "$@" # 如果传入其他命令(如 bash),则容器的作用就成了执行该命令,而非启动 DBMS
作用:
1
,以告知 Docker 守护进程容器的运行状态。# 暴露端口号
EXPOSE 端口号
# 设置工作目录
WORKDIR 目录
# 设置用户,限制权限
USER 用户名
docker commit
- 根据容器生成镜像docker commit [选项] 容器标识符 镜像名[:标签]
不太推荐这么做,因为体积会比较大。
如不指定标签则为 latest
。
选项:
-a 作者
-m 信息
-c "Dockerfile指令"
:同时将指令写入镜像(如有多条指令,依次重复使用)root@ding-server:~# docker run -dit --name ubuntu_test ubuntu
root@ding-server:~# docker exec -it ubuntu_test bash
# 接下来在 ubuntu_test 容器中
root@0f14368df114:/# sed -i \
> 's/http:\/\/archive.ubuntu.com\/ubuntu\//http:\/\/mirrors.fsc.efoxconn.com\/apt\//g' \
> /etc/apt/sources.list
root@0f14368df114:/# sed -i \
> 's/http:\/\/security.ubuntu.com\/ubuntu\//http:\/\/mirrors.fsc.efoxconn.com\/apt\//g' \
> /etc/apt/sources.list
root@0f14368df114:/# apt update && apt upgrade
root@0f14368df114:/#
exit
# 接下来在宿主机
root@ding-server:~# docker commit -m 'ding update test' -a 'dingjunyao' \
> ubuntu_test dingjunyao/ubuntu:test
sha256:d0bca05c69d17c056d14929aff21d4df53ca5ce85c675d9cd3825fe3ac2c88a9
root@ding-server:~# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
dingjunyao/ubuntu test d0bca05c69d1 About a minute ago 115MB
root@ding-server:~# docker image history dingjunyao/ubuntu:test
IMAGE CREATED CREATED BY SIZE COMMENT
d0bca05c69d1 About a minute ago bash 37.1MB ding update test
27941809078c 7 days ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 7 days ago /bin/sh -c #(nop) ADD file:11157b07dde10107f… 77.8M
镜像存为文件:
docker save 镜像 > 要存为的文件.tar
从文件读取镜像:
docker load < 要导入的文件.tar
可用于网络无法访问镜像库的情况,通过能够访问镜像库的主机下载镜像,传至无法访问镜像库的主机。
docker tag 镜像名 新镜像名
其中新镜像名一般的格式如下:
[镜像库地址/][用户名/]镜像名[:标签名]
镜像库地址可以带端口号。
docker login [选项] [镜像库地址]
如不指定镜像库地址,则登录的是 Docker 官方的镜像库。
选项:
-u 用户名
-p 密码
--password-stdin
:从标准输入读取密码退出:
docker logout [镜像库地址]
docker push [选项] 镜像名
如镜像名不填镜像库地址,则为 Docker 官方镜像库。
选项:
--all-tags
:推送给定镜像下的所有标签其他能够访问镜像库的主机可以拉取镜像。同样先需要登录。
如已有同标签名(特别是 latest)的镜像,需先删掉。