docker镜像最小化实践(php-cli 为例)
docker的镜像有大又小,大的镜像在于功能齐全,扩展方便,实操性更多,而小的镜像则在于麻雀虽小,五脏俱全,部署方便,代价小。
最近在工作中发现每次生成镜像极为缓慢和庞大,然后抽空对之前写的Dockefile做了梳理,重新写了一下,把镜像构建优化了30%,镜像大小缩小了2.5倍。
实践展示
镜像前后对比基础包的构建
目前我们很多镜像都基于Ubuntu构建,但是我们在构建镜像上还是过于臃肿,所以我们采用 alpine linux 来作为基础镜像,可以看看下面的对比。
ubuntu基础镜像
alpine 基础镜像
使用alpine需要注意的一点是我们的包管理工具变成了apk,需要通过apk进行操作,而alpine 默认的源是http://dl-cdn.alpinelinux.org/,其实也不是很慢,但是如果觉得速度不满意的,可以替换为aliyun
#编辑源文件 /etc/apk/repositories
https://mirrors.aliyun.com/alpine/v3.6/main/
https://mirrors.aliyun.com/alpine/v3.6/community/
apk的使用
大家可以参考这篇文章,在这里不做详讲。https://blog.csdn.net/liupeifeng3514/article/details/80418887
php 基础镜像的选择
我所需要的php版本是php7.2,所以我一开始采用的是php:7.2的镜像,大约有200MB左右,后面特地去docker hub上查找,发现了php:7.2.10-cli-alpine3.8基础镜像,完全基于alpine环境,这个镜像大概有78MB左右,可能大概有人还觉得比较大,那可以自己基于alpine来构建,我在github找到了一个自己写的版本,构建后大概40MB左右,可惜的是7.1版本的,所以最后我没采用,使用还是官方的版本。
这里附下链接,https://github.com/swoftcloud/alphp/blob/alpine3.8/alphp-base.Dockerfile。
时区的设置
RUN apk add -U tzdata \
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& apk del tzdata
因为默认的时区是UTC,所以我们需要搭建镜像时就变成中国时区
基础库的配置变更
我们现在需要把我们之前搭建基础库的方式从apt-get变更为apk获取,比如
RUN apt-get update \
&& apt-get install -y \
curl \
wget \
git \
zip \
librdkafka-dev \
&& apt-get clean \
&& apt-get autoremove
这是我们之前的ubuntu的
RUN set -ex \
&& apk update \
&& apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS \
&& apk add --no-cache libaio \
openssl-dev \
librdkafka-dev \
&& apk del .phpize-deps \
&& rm -rf /var/cache/apk/* /tmp/* /usr/share/man
这个则是替换为alpine的方式,因为curl在php的基础镜像里已经有了,所以我没有重新添加。
这里有一点很重要,即时你用了很基础很基础的镜像,也最好学会阅读它的Dockerfile,至少不会让你重复在安装一遍一些比较基础的东西。
怎么查看基础镜像已经具备的功能或者命令?
当然是基于镜像构建一个容器,进去试试咯
docker run -it php:7.2.10-cli-alpine3.8 sh
可以进到容器里的sh,然后你就可以试试了
扩展的安装
当我们把之前讲的都写进Dockerfile后,可以先build一个镜像出来,我们把它叫做my:base。
docker build . -t my:base
然后建一个容器进去执行php -m,看看我们安了哪些扩展,发现扩展貌似缺了几个我们想要的,那我们就得通过Dockerfile来安装我们的扩展,假设我们还需要安装swoole,redis这两个扩展。
因为版本可能会存在更新,所以我们会把版本号单独拿出来作为一个常量,哇,方便多了。
ENV SWOOLE_VERSION=4.1.2 \
REDIS_VERSION=4.0.2
接下来就是我们下载安装我们的扩展
RUN set -ex \
&& cd /tmp \
&& curl -SL "https://github.com/swoole/swoole-src/archive/v${SWOOLE_VERSION}.tar.gz" -o swoole.tar.gz \
&& curl -SL "http://pecl.php.net/get/redis-${REDIS_VERSION}.tgz" -o redis.tgz \
# php extension: redis
&& pecl install redis.tgz \
&& docker-php-ext-enable redis \
# php extension: swoole
&& cd /tmp \
&& mkdir -p swoole \
&& tar -xf swoole.tar.gz -C swoole --strip-components=1 \
&& rm swoole.tar.gz \
&& ( \
cd swoole \
&& phpize \
&& ./configure --enable-mysqlnd --enable-coroutine --enable-openssl \
&& make -j$(nproc) && make install \
) \
&& docker-php-ext-enable swoole
可以看到我们先把网络文件全部下载到本地才开始安装,当然为了方便我们可以使用COPY命令把安装文件先下载好,拷贝进去。pecl 也是之前安装好的命令,所以我们直接安装,然后把扩展加载到php.ini可以直接用docker(docker-php-ext-enable)命令,感觉真的是极为友好方便了。至于swoole为什么不用pecl,因为我在configure阶段需要可配置项,所以没有。
一些常用的扩展我们还可以通过下面的方式来安装
docker-php-ext-install pdo_mysql
至此
至此,我们的一个基础的php镜像搭建完成了,我个人建议将镜像分为两部分,一部分是基础镜像,基本不会改变的,一部分是业务镜像,属于经常变更的。
业务镜像的构建
这个就根据自己的业务来搭建了,假设我的Dockerfile存放在我的项目根目录下
ADD . /var/www/my
WORKDIR /var/www/my
然后我们的项目是依赖于composer的,但是代码仓库为了源代码尽量的小且整洁,我们默认是不上传vendor目录的,所以需要我们安装composer 引入依赖的包。
RUN curl -sS https://getcomposer.org/installer | php \
&& mv composer.phar /usr/local/bin/composer \
&& apk add git \
&& composer self-update --clean-backups \
&& composer install --no-dev \
&& composer dump-autoload -o \
&& composer clearcache \
&& apk del git \
&& rm -rf /var/cache/apk/* /tmp/* /usr/share/man
至于这个时候才安装git,就是因为composer需要,不需要则立马卸载,而之前那些我下载的包都放在了/tmp目录下,所以我也同时删除了所有包,这也就是我们镜像的特点,干净,整洁,统一管理。
启动我们的cli
EXPOSE 80
CMD ["php", "/var/www/my/index", "start"]
最后当然是声明我们容器的端口并且启动咯。
当然启动的命令就可以参照下面格式了
docker run -d -p 10002:80 --restart=always -u="root" -v /apps/:/apps -v --name "my-php" my:latest
my:latest就是我们的业务镜像了。
最后
把我们完整的镜像文件贴出来给大家参考:
my.base.Dockerfile
# docker build . -f my-base.Dockerfile -t my:base
FROM php:7.2.10-cli-alpine3.8
LABEL maintainer="失忆的决 <masixun71@foxmail.com>" version="1.0"
ENV SWOOLE_VERSION=4.1.2 \
REDIS_VERSION=4.0.2 \
#Timezone and lib
RUN apk add -U tzdata \
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& apk del tzdata
RUN set -ex \
&& cd /tmp \
&& curl -SL "https://github.com/swoole/swoole-src/archive/v${SWOOLE_VERSION}.tar.gz" -o swoole.tar.gz \
&& curl -SL "http://pecl.php.net/get/redis-${REDIS_VERSION}.tgz" -o redis.tgz \
&& ls -alh \
&& apk update \
&& apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS \
# for swoole ext
&& apk add --no-cache libaio \
linux-headers \
libaio-dev \
openssl-dev \
libstdc++ \
librdkafka-dev \
# php extension: redis
&& pecl install redis.tgz \
&& docker-php-ext-enable redis \
# php extension: pdo_mysql
&& docker-php-ext-install pdo_mysql \
# php extension: bcmath
&& docker-php-ext-install bcmath \
# php extension: swoole
&& cd /tmp \
&& mkdir -p swoole \
&& tar -xf swoole.tar.gz -C swoole --strip-components=1 \
&& rm swoole.tar.gz \
&& ( \
cd swoole \
&& phpize \
&& ./configure --enable-async-redis --enable-mysqlnd --enable-coroutine --enable-openssl \
&& make -j$(nproc) && make install \
) \
&& rm -r swoole \
&& docker-php-ext-enable swoole \
&& apk del .phpize-deps \
Dockerfile
FROM my:base
LABEL maintainer="失忆的决<masixun71@foxmail.com>" version="1.0"
ADD . /var/www/my
WORKDIR /var/www/my
RUN curl -sS https://getcomposer.org/installer | php \
&& mv composer.phar /usr/local/bin/composer \
&& apk add git \
&& composer self-update --clean-backups \
&& composer install --no-dev \
&& composer dump-autoload -o \
&& composer clearcache \
&& apk del git \
&& rm -rf /var/cache/apk/* /tmp/* /usr/share/man
EXPOSE 80
CMD ["php", "/var/www/my/index", "start"]
要注意的点
1.docker 支持的层级是有限的,层级越少,越利于维护和镜像大小的降低,所以尽量把多层合并为1层。
2.把不再需要的东西做好删除,不然会一直存放在镜像中。
3.镜像注意划分基础镜像和业务镜像,提高复用性和可维护性。