制作Docker基础镜像

每次使用的docker镜像都是基于它人制作好的镜像,如何想要自己定制一个镜像。

在虚拟机中安装好系统,然后进入该系统中将某些目录的文件打成tar包,再通过docker的import命令来创建镜像

打包时其中/proc/sys/run/dev这几个目录都是系统启动时自动生成的,虽然也属于文件系统一部分,但是他们每次开机都会有变化,所以打包的时候就应该忽略它们。

tar -cvpf /tmp/base_system.tar --directory=/ --exclude=proc --exclude=sys --exclude=dev --exclude=run --exclude=boot

需要注意的是,在打包之前,请先确认要将哪些文件进行打包,比如下面这些目录都可以不用打包:

某些用户的home目录是否需要打包 每个用户的执行的历史命令是否需要清除 一些涉及到隐私的文件 与系统镜像没有关系的文件,或者说只是系统运行过程中保存的垃圾文件或者缓存等 临时文件 建议手动选择要进行打包的目录,比如:bin dev lib64 media opt root snap var boot etc lib mnt sbin srv usr vmlinuz这些目录 如果你拿不准哪些文件目录要打包,那么可以将根目录下所有文件都打包(不推荐)

cd / 
tar -cf  base_system.tar  bin dev lib64 media opt root snap var boot etc lib mnt sbin srv usr vmlinuz

打包部分目录

tar -cf  base_system.tar bin  etc  home  lib  lib64  media  mnt  opt  root  sbin  srv  usr  var

cat base_system.tar | docker import - ubuntu:18.04

dockerfile 中 复制当前目录下的目录到指定目录下并保持目录名不变

在 Dockerfile 中,你可以使用 COPY 指令将当前目录下的目录复制到指定的目录,并保持目录名称不变。例如,假设你希望将当前目录下的 myfolder 复制到 Docker 镜像的 /app 目录下,你可以使用以下的 Dockerfile 指令:

COPY myfolder /app/myfolder

这将会创建一个新的目录 /app/myfolder 并且将 myfolder 目录的所有文件复制到这个新目录。

如果你希望复制当前目录下的所有文件和目录到镜像的 /app 目录,你可以使用以下的 Dockerfile 指令:

COPY . /app
注意:你的 Dockerfile 以及需要复制的文件/目录必须在同一个 context 中。当你运行 docker build 命令时,你可以通过 . 指定 context,例如 docker build .,这样 Docker 就会使用当前目录作为 context。

构建时增加支持中文、调整时区

基于CentOS 7镜像增加支持中文、调整时区

yum install -y kde-l10n-Chinese安装中文包 localedef -c -f UTF-8 -i zh_CN zh_CN.utf8安装完中文包后应用 ENV LC_ALL "zh_CN.UTF-8"配置环境变量 ENV TZ Asia/Shanghai配置时区

FROM centos:7.9.2009
WORKDIR /tmp/
COPY *gz start.sh /tmp/
RUN yum install -y unzip wget curl bash net-tools kde-l10n-Chinese && \
    yum -y reinstall glibc-common && \
    yum clean all && \
    localedef -c -f UTF-8 -i zh_CN zh_CN.utf8
RUN tar -zxf /tmp/jdk-8u171-linux-x64.tar.gz -C /home
ENV LC_ALL "zh_CN.UTF-8"
ENV JAVA_HOME=/home/jdk1.8.0_171
ENV JRE_HOME=/home/jdk1.8.0_171/jre
ENV CLASSPATH=.:/home/jdk1.8.0_171/lib:/home/jdk1.8.0_171/jre/lib
ENV TZ Asia/Shanghai
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/jdk1.8.0_171/bin
RUN chmod +x /tmp/start.sh && tar -zxf app.tar.gz -C / && rm -rf /tmp/*gz
CMD ["sh", "start.sh"]

基于Alpine 镜像构建时调整时区

FROM alpine:latest
...
RUN apk update && apk add tzdata
ENV TZ Asia/Shanghai
...

在k8s编排文件中配置

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: APP-NAME
  namespace: NAMESPACES
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: APP-NAME
    spec:
      containers:
      - name: APP-NAME
        image: alpine:latest
		# 增加时区的环境变量
        env:
        - name: TZ
          value: Asia/Shanghai
		...

CentOS官方基础镜像构建

有时出于安全、精简考虑需要基于CentOS系统的纯净基础镜像构建应用的镜像,可以参考CentOS官方在GitHub上的这个项目

选择想要系统版本的分支,检出代码进行构建 下面以CentOS7最新版(7.9.2009版本)为例

git clone https://github.com/CentOS/sig-cloud-instance-images.git
ls sig-cloud-instance-images/docker
cccp.yaml  centos-7-x86_64-docker.tar.xz  Dockerfile

下面是Dockerfile内容,可以在该文件的基础上再添加自己需要的软件

FROM scratch
ADD centos-7-x86_64-docker.tar.xz /
LABEL \
    org.label-schema.schema-version="1.0" \
    org.label-schema.name="CentOS Base Image" \
    org.label-schema.vendor="CentOS" \
    org.label-schema.license="GPLv2" \
    org.label-schema.build-date="20201113" \
    org.opencontainers.image.title="CentOS Base Image" \
    org.opencontainers.image.vendor="CentOS" \
    org.opencontainers.image.licenses="GPL-2.0-only" \
    org.opencontainers.image.created="2020-11-13 00:00:00+00:00"

CMD ["/bin/bash"]

Dockerfile中ENTRYPOINT和CMD的用法

简单的区别就是ENTRYPOINT执行的内容在容器启动时不会被指定的命令覆盖,而CMD则会 示例:

FROM centos
ENTRYPOINT ["/bin/ping", "-c", "3"]
CMD ["localhost"]

根据上面的Dockerfile构建镜像,不带参数启动容器时,会执行ping -c 3 localhost。 但如果我想用这个容器来ping另外一个地址时就不用重新构建,只需要在docker run的时候加上新地址即可

docker run IMAGE_NAME www.baidu.com

上面的命令运行的容器就会执行ping -c 3 <www.baidu.com>DockerfileCMD定义的localhost就被替换成了www.baidu.comENTRYPOINT定义的内容则不会被修改

另外,ENTRYPOINCMDRUN运行命令都支持两种写法分别是shellexec

Shell形式的ENTRYPOINT忽略任何CMDdocker运行命令行参数 shell写法示例:

RUN apt update
ENTRYPOINT ping -c 3 localhost
CMD bash

ENTRYPOINTExec形式允许您设置命令和参数,然后使用任一形式的CMD来设置更可能更改的其他参数。使用ENTRYPOINT参数,而可以通过Docker容器运行时提供的命令行参数覆盖CMDexec写法示例:

ENTRYPOINT ['ping', '-c', '3']
CMD ['localhost']

CMD ['ping', 'localhost']

注意:ENTRYPOINTCMD 不能同时为字符串值。 它们可以都是列表,或者 ENTRYPOINT 为一个列表而 CMD 为一个字符串值;但如果 ENTRYPOINT 为一个字符串值,则 CMD 将被忽略。这是将参数字符串转换为列表后无法避免的不幸后果。

一般原则

如果说今天您可以学到一个经验 ,那就是要遵循以下一般原则:

ENTRYPOINT + CMD = 默认容器命令参数

ENTRYPOINT 也可覆盖

假设我们拥有如下的 Dockerfile,并从它创建了一个叫做 myservice 的映像:

ENTRYPOINT ["/bin/chamber", "exec", "production", "--"]
CMD ["/bin/service", "-d"]

通过运行如下命令来修改 ENTRYPOINT:

docker run --entrypoint /bin/sh myservice

同时覆盖 ENTRYPOINT 和 CMD

我们能否同时覆盖 ENTRYPOINT 和 CMD? 当然可以:

docker run --entrypoint /bin/sh myservice /bin/service -e

JDK镜像示例

示例一

tree ./ -a 
├── app.tar.gz
├── centos-7-x86_64-docker.tar.xz
├── Dockerfile
├── jdk-8u171-linux-x64.tar.gz
└── start.sh

tree app
app
├── config
├── jp-pjj3-core-3.jar
└── lib

Dockerfile

FROM scratch
ADD centos-7-x86_64-docker.tar.xz /
LABEL \
    org.label-schema.schema-version="1.0" \
    org.label-schema.name="CentOS Base Image" \
    org.label-schema.vendor="CentOS" \
    org.label-schema.license="GPLv2" \
    org.label-schema.build-date="20201113" \
    org.opencontainers.image.title="CentOS Base Image" \
    org.opencontainers.image.vendor="CentOS" \
    org.opencontainers.image.licenses="GPL-2.0-only" \
    org.opencontainers.image.created="2020-11-13 00:00:00+00:00"

CMD ["/bin/bash"]
ADD jdk-8u171-linux-x64.tar.gz /home/
ENV JAVA_HOME=/home/jdk1.8.0_171
ENV JRE_HOME=/home/jdk1.8.0_171/jre
ENV CLASSPATH=.:/home/jdk1.8.0_171/lib:/home/jdk1.8.0_171/jre/lib
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/jdk1.8.0_171/bin
ENV TZ Asia/Shanghai
RUN yum install -y unzip wget curl bash net-tools kde-l10n-Chinese && \
    yum -y reinstall glibc-common && \
    yum clean all && \
    localedef -c -f UTF-8 -i zh_CN zh_CN.utf8 
ENV LC_ALL "zh_CN.UTF-8"
ADD app.tar.gz /
COPY start.sh /tmp/
RUN chmod +x /tmp/start.sh
CMD ["sh", "/tmp/start.sh"]

start.sh

#! /bin/bash
cd /app/
mkdir -p /tmp/logs
nohup java -jar *.jar >> /tmp/logs/catalina.out 2>&1 
tail -f /tmp/logs/catalina.out
docker build -t jdk:v1.8.171.2021.12.07 ./

示例二

FROM ubuntu:22.04

RUN	apt update && \
	apt install -y wget curl tar && \
	apt clean
# 官网下载旧版本需要登录。这里配置的jdk下载地址为Cloudflare R2地址。
RUN wget https://pub-2f882d5e724749a382228f2e5512db91.r2.dev/jdk-8u381-linux-x64.tar.gz && \
	tar -zxf jdk-8u381-linux-x64.tar.gz -C /usr/local/ && \
	rm -v jdk-8u381-linux-x64.tar.gz
ENV TZ=Asia/Shanghai
ENV JAVA_HOME /usr/local/jdk1.8.0_381
ENV PATH $JAVA_HOME/bin:$PATH
ENV CLASSPATH .:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
CMD ["java","-version"]

复制项目后的启动

FROM jdk:v1.8.381
ADD project_name /app
CMD ["java", "-jar", "project_name.jar"]

start.sh

#! /bin/bash
cd /app/
mkdir -p /tmp/logs
nohup java -jar *.jar >> /tmp/logs/catalina.out 2>&1 

最后更新于