本文是我在学习《Docker技术入门与实战》第3版(杨保华 戴王剑 曹亚仑编著)时所做的笔记,有许多描述不清楚的地方,建议自行购买学习。
使用Dockerfile创建镜像
基本结构
Dockerfile主题内容分为四部分:
1、基础镜像信息;
2、维护者信息;
3、镜像操作指令;
4、容器启动时执行指令。
Dockerfile支持以#开头的注释行。
例如之前构建镜像用的Dockerfile:
# 基础镜像
From ubuntu
# 维护者信息和版本号
LABEL verison="1.1" maintainer="duanhun"
# 镜像操作指令
RUN apt-get update && \
apt-get install -y git && \
apt-get install -y nginx && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# 之前没用到的容器启动时执行指令,也就是说之前安装的nginx就没启动,之前也只是验证了下版本
CMD /usr/sbin/nginx
指令说明
Dockerfile中指令分为配置指令和操作指令。
配置指令
1、ARG
定义创建镜像过程中使用的变量。
格式为ARG [=]
执行docker build时可通过-build-arg[=]来为变量赋值。
Docker内置了一些镜像创建变量,用户可以直接使用而无须声明,包括(不区分大小写)HTTP_PROXY、HTTPS_PROXY、FTP_PROXY、NO_PROXY。
2、FROM
指定所创建镜像的基础镜像。
为了保证镜像精简,尽量选用体积较小的镜像如Alpine或Debian作为基础镜像。
3、LABEL
LABEL 指令可以为生成的镜像添加元数据标签信息。
格式为LABEL <key>=<value> <key>=<value> …
4、EXPOSE
声明镜像内服务监听的端口。
格式为EXPOSE <port> [<port>/<protocol>…]
该指令只是起到声明作用,并不会自动完成端口映射。
5、ENV
指定环境变量,在镜像生成过程中会被后续RUN指令使用,在镜像启动的容器中也会存在。
格式与LABEL一样。
指定的环境变量会被命令参数docker run --env <key>=<value> 覆盖。
6、ENTRYPOINT
指定镜像的默认入口命令,该入口命令会在启动容器时作为根命令执行。
在运行时可以被–entrypoint参数覆盖掉。
7、VOLUME
创建一个数据卷挂载点。
8、USER
指定运行容器时的用户名或UID。
9、WORKDIR
为后续的RUN、CMD、ENTRYPOINT指令配置工作目录。
10、ONBUILD
指定当基于所生成镜像创建子镜像时,自动执行的操作指令。
11、STOPSIGNAL
指定所创建镜像启动的容器接收退出的信号值。
12、HEALTHCHECK
配置所启动容器如何进行健康检查。
13、SHELL
指定其他命令所使用shell时的默认shell类型。
操作指令
1、RUN
运行指定命令。
格式为RUN 或RUN [“executable”, “param1”, “param2”]。前者默认将在shell终端中运行;后者则使用exec执行,不会启动shell环境。
2、CMD
指令用来指定启动容器时默认执行的命令。
3、ADD
添加内容到镜像。
格式ADD <src> <dest>
将复制指定的<src>路径下的内容到容器中的<dest>路径下。
4、COPY
复制内容到镜像。
复制本地主机的<src>下内容到镜像中的<dest>。目标路径不存在时会自动创建。
当使用本地目录作为原目录时推荐使用COPY。
创建镜像
docker build命令用于编译Dockerfile文件并生成镜像,子命令-t用来指定生成镜像的名称和版本号,具体信息可参考docker build –help。
选择父镜像
用户可以选择两种镜像作为父镜像,一种是基础镜像,另一种是普通镜像。
基础镜像比较特殊,其Dockerfile中往往不存在FROM指令,或者基于scratch镜像。
使用.dockerignore文件
可以通过.dockerignore文件来让Docker忽略匹配路径或文件,在创建镜像时不将无关数据发送到服务器。
例如:
# .dockerignore文件中可以定义忽略模式
*/temp*
*/*/temp*
tmp?
~*
Dockerfile
!README.md
多步骤创建
对于需要编译的应用来说,通常情况下至少需要准备两个环境的Docker镜像:
1、编译环境镜像;
2、运行环境镜像。
使用多步骤创建,可以在保证最终生成的运行环境镜像保持精简的情况下,使用单一的Dockerfile,降低维护复杂度。
实践经验
1、精简镜像用途;
2、选用合适的基础镜像;
3、提供注释和维护者信息;
4、减少镜像层数:尽量合并RUN、ADD和COPY指令;
5、恰当使用多步骤创建;
6、使用.dockerignore文件;
7、及时删除临时文件和缓存文件;
8、提高生成速度;
9、调整合理的指令顺序;
10、减少外部源的干扰。
操作系统
BusyBox
BusyBox是一个集成了100多个最常用linux命令的精简工具箱,只有不到2MB,被誉为“linux系统的瑞士军刀”。
获取和运行BusyBox:
docker run -it busybox
Alpine
Alpine系统时一个面向安全的轻型Linux发行版,关注安全、性能和资源效能。目前Docker官方推荐使用Alpine作为默认的基础镜像环境。
其他系统自己下载看吧,Debian、Ubuntu、CentOS、Fedora。
为镜像添加SSH服务
基于commit命令创建
Docker提供了docker commit命令,支持用户提交自己对指定容器的修改。
在此采用alpine作为基础镜像来添加SSH服务。
1、准备工作
本地先生成一份ssh-keygen密钥,执行命令,然后一路回车键:
ssh-keygen
新建一个目录,然后将~/.ssh/id_rsa.pub文件拷贝进取。
mkdir sshd_alpine
cp ~/.ssh/id_rsa.pub sshd_alpine/authorized_keys
下载并启动alpine,执行命令:
docker run -it -v /home/duanhun/projects/docker/sshd_alpine/:/volume alpine
2、配置软件源
执行命令:
echo "http://mirrors.aliyun.com/alpine/v3.9/main" > /etc/apk/repositories
echo "http://mirrors.aliyun.com/alpine/v3.9/community" >> /etc/apk/repositories
apk update
3、安装和配置SSH服务
执行命令安装openssh-server:
apk add openssh-server
生成ssh-keygen密钥,执行命令
ssh-keygen -t rsa -N "" -f /etc/ssh/ssh_host_rsa_key
ssh-keygen -t ecdsa -N "" -f /etc/ssh/ssh_host_ecdsa_key
ssh-keygen -t ed25519 -N "" -f /etc/ssh/ssh_host_ed25519_key
设置允许root用户从ssh登录
echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
把root密码设置成空字符串,不执行的话root无法通过ssh登录
passwd -d root
将/volume下的文件拷贝到/root/.ssh/目录下
mkdir ~/.ssh
cp /volume/authorized_keys ~/.ssh/.
启动SSH服务
/usr/sbin/sshd -D &
4、验证SSH服务
执行ifconfig查看容器ip地址:
另外打开一个客户端,执行命令:
ssh -p22 root@172.17.0.2
5、执行docker commit提交镜像
docker commit 305e5cf32a54 sshd:alpine
6、测试镜像
停止和删除之前的alpine容器,从新建的镜像重新启动一个容器:
docker run -d -p 10022:22 sshd:alpine /usr/sbin/sshd -D
继续执行:
ssh -p10022 root@127.0.0.1
使用Dockerfile创建
也就是把上述命令整理起来再做一些补充即可。
在上边新建的sshd_alpine目录下新建Dockerfile文件,内容如下:
# ssh版本号
ARG sshd_version=7.9_p1-r5
# alpine版本号
ARG alpine_version=3.10.2
# root用户密码
ARG root_passwd=123456
# 基础镜像alpine
FROM alpine:${alpine_version}
# 官网上有这句话:An ARG declared before a FROM is outside of a build stage, so it can’t be used in any instruction after a FROM. To use the default value of an ARG declared before the first FROM use an ARG instruction without a value inside of a build stage:
# google翻译:在FROM之前声明的ARG在构建阶段之外,因此,FROM之后的任何指令都不能使用它。 要使用在第一个FROM之前声明的ARG的默认值,请使用ARG指令,而在构建阶段内部不带值
# 官方说明地址: https://docs.docker.com/engine/reference/builder/
# 这个坑踩了一星期终于踩平了
ARG sshd_version
ARG root_passwd
# 镜像信息
LABEL version=0.1 maintainer=duanhun
# 配置软件源
RUN echo "http://mirrors.aliyun.com/alpine/v3.9/main" > /etc/apk/repositories \
&& echo "http://mirrors.aliyun.com/alpine/v3.9/community" >> /etc/apk/repositories \
&& apk update
# 安装和配置ssh服务,并删除缓存
RUN apk add openssh-server=${sshd_version} \
&& ssh-keygen -t rsa -N "" -f /etc/ssh/ssh_host_rsa_key \
&& ssh-keygen -t ecdsa -N "" -f /etc/ssh/ssh_host_ecdsa_key \
&& ssh-keygen -t ed25519 -N "" -f /etc/ssh/ssh_host_ed25519_key \
&& echo "PermitRootLogin yes" >> /etc/ssh/sshd_config \
&& rm -rf /var/cache/apk/
# root用户处理
RUN echo -e "${root_passwd}\n${root_passwd}" | passwd root
# COPY命令可自动创建目标目录,ADD不行
COPY authorized_keys /root/.ssh/
# 声明端口号
EXPOSE 22
# 容器启动时启动ssh服务
CMD /usr/sbin/sshd -D
此时ssh_alpine目录下的文件:
执行命令:
docker build -t sshd:dockerfile .
继续测试运行:
6、测试镜像
从新建的镜像启动一个容器,不需要指定启动时执行的命令:
docker run -d -p 10022:22 sshd:dockerfile
继续执行:
ssh -p10022 root@127.0.0.1
需要清理下之前链接留下的信息才能正常连接。
在容器中执行ps -a,可以看到SSH服务的进程号为1: