参考

1、容器应用指南-第一个容器

3、容器应用指南-Docker 基本概念

4、Kubernetes 和云原生

镜像选择

镜像名称版本标记大小备注

busybox

latest1.22M
alpine3.9.55.33M优先考虑
CentOS7.9300M
Debianbuster114M

制作docker镜像时,使用 multi stage 构建各个不同阶段的命令文件,最终copy到busybox或alpilne中执行。这样做得好处有二:

1、保持镜像最小,busybox,alpine 是最小的操作系统镜像

2、有各种linux工具使用,busybox本身是一个linux工具集

构建stage建议使用alpine和其它的官方包, 主要理由是此镜像比busybox稍大,但是比其他的系统镜像都小。busybox没有包管理工具,带来很多不便;这也是busybox和alpine的主要区别。

创建基础镜像

Docker 的镜像实际上由一层一层的文件系统组成,这种层级的文件系统就是上文说到的UnionFS。

在Docker镜像的最底层是bootfs。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。

当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。

Docker在bootfs之上的一层是rootfs(根文件系统)。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。

Docker 核心技术与实现原理 这篇文章,作者阅读了 rootfs 的规范,指出构建rootfs一些必须的文件夹。

创建 alpine 镜像

可以通过下载离线文件的方式构建,也可以通过 Docker 下载后再构建。区别在于 Dockerfile 文件中的配置语句的引用。

可以直接通过alpine 的网站下载离线文件。

docker pull alpine
docker images

离线文件,直接下载到本地。

容器在线配置

执行 docker build 命令时,当前的工作目录被称为构建上下文。默认情况下,Dockerfile 就位于该路径下。也可以通过 -f 参数来指定 dockerfile ,但 docker 客户端会将当前工作目录下的所有文件发送到 docker 守护进程进行构建。

确保构建目录内容干净
mkdir /opt/alpine
cat > /opt/alpine/Dockerfile <<EOF
FROM alpine:latest

RUN set -x \
    && echo "http://mirrors.huaweicloud.com/alpine/latest-stable/main/" > /etc/apk/repositories \
    && echo "http://mirrors.huaweicloud.com/alpine/latest-stable/community/" >> /etc/apk/repositories \
    && apk update \
    && apk add --no-cache tzdata \
    && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
    && echo "Asia/Shanghai" > /etc/timezone \
    && apk del tzdata 

容器离线配置

离线文件,构建文件的引用内容如下

FROM scratch
ADD alpine-minirootfs-3.15.0-x86_64.tar.gz
CMD ["/bin/sh"]

Dockerfile 中相关参数

FROM 指的是依赖的基础镜像,如scratch表示的是空白的,从零开始的。依赖的镜像可以是本地的,也可以是远程库的

ADD 指的是添加本地文件到镜像中,如果遇到linux可解压格式文件,会自动解压,这就是为什么整个文件中没有对tar.gz进行显式解压

RUN 运行命令,如安装软件的相关命令

CMD 设置启动Container时默认执行的命令,这个可以在启动容器时覆盖

构建镜像

docker build -t alpine:tz2 .
docker run --rm -ti alpine:tz2 date
docker run  -ti alpine:tz2 /bin/sh

经验

尽量使用 URL 添加源码

如果不采用分阶段构建,对于一些需要在容器内进行编译的项目,最好通过 git 或者 wegt 的方式将源码打入到镜像内,而非采用 ADD 或者 COPY ,因为源码编译完成之后,源码就不需要可以删掉了,而通过 ADD 或者 COPY 添加进去的源码已经用在下一层镜像中了,是删不掉的。

也就是说 git & wget source 然后 build ,最后 rm -rf source/ 这三部放在一条 RUN 指令中,这样就能避免源码添加到镜像中而增大镜像体积。

使用虚拟编译环境

对于只在编译过程中使用到的依赖,可以将这些依赖安装在虚拟环境中,编译完成之后可以一并删除这些依赖,比如 alpine 中可以使用 apk add --no-cache --virtual .build-deps ,后面加上需要安装的相关依赖。

apk add --no-cache --virtual .build-deps gcc libc-dev make perl-dev openssl-dev pcre-dev zlib-dev git

构建完成之后可以使用 apk del .build-deps 命令,一并将这些编译依赖全部删除。需要注意的是,.build-deps 后面接的是编译时以来的软件包,并不是所有的编译依赖都可以删除,不要把运行时的依赖包接在后面,最好单独 add 一下。

最小化层数

docker 在 1.10 以后,只有 RUN、COPY 和 ADD 指令会创建层,其他指令会创建临时的中间镜像,但是不会直接增加构建的镜像大小了。

前文提到了建议使用 git 或者 wget 的方式来将文件打入到镜像当中,但如果我们必须要使用 COPY 或者 ADD 指令呢?

多个文件需要添加到容器中不同的路径,每个文件使用一条 ADD 指令的话就会增加一层镜像,这样就多了 12 层镜像。

# centos 7
FROM centos:7
# 添加配置文件
# add profiles
ADD conf/client.conf /etc/fdfs/
ADD conf/http.conf /etc/fdfs/
ADD conf/mime.types /etc/fdfs/
ADD conf/storage.conf /etc/fdfs/
ADD conf/tracker.conf /etc/fdfs/
ADD fastdfs.sh /home
ADD conf/nginx.conf /etc/fdfs/
ADD conf/mod_fastdfs.conf /etc/fdfs

# 添加源文件
# add source code
ADD source/libfastcommon.tar.gz /usr/local/src/
ADD source/fastdfs.tar.gz /usr/local/src/
ADD source/fastdfs-nginx-module.tar.gz /usr/local/src/
ADD source/nginx-1.15.4.tar.gz /usr/local/src/

其实大可不必,可以将这些文件全部打包为一个文件为 src.tar.gz 然后通过 ADD 的方式把文件添加到当中去,然后在 RUN 指令后使用 mv 命令把文件移动到指定的位置。这样仅仅一条 ADD 和 RUN 指令取代掉了 12 个 ADD 指令 。

FROM alpine:3.15
COPY src.tar.gz /usr/local/src.tar.gz
RUN set -xe \
&& apk add --no-cache --virtual .build-deps gcc libc-dev make perl-dev openssl-dev pcre-dev zlib-dev tzdata \
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& tar -xvf /usr/local/src.tar.gz -C /usr/local \
&& mv /usr/local/src/conf/fastdfs.sh /home/fastdfs/ \
&& mv /usr/local/src/conf/* /etc/fdfs \
&& chmod +x /home/fastdfs/fastdfs.sh \
&& rm -rf /usr/local/src/* /var/cache/apk/* /tmp/* /var/tmp/* $HOME/.cache
VOLUME /var/fdfs

LNMP 环境

1、基于 PHP7.4.28 版本,添加 PHP-GD 扩展

2、Nginx 配置最新版本

3、Mysql 使用 Mariadb10版本

自定义 php 环境

sudo mkdir /opt/php
sudo chown docker /opt/php
cat > /opt/php/Dockfile << EOF
FROM php:7.4.28-fpm-alpine3.15

RUN set -x \
        && echo "http://mirrors.huaweicloud.com/alpine/latest-stable/main/" > /etc/apk/repositories \
        && echo "http://mirrors.huaweicloud.com/alpine/latest-stable/community/" >> /etc/apk/repositories \
        && apk update \
        && apk add --no-cache tzdata \
        && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
        && echo "Asia/Shanghai" > /etc/timezone \
        && apk del tzdata \
        && apk add --no-cache graphviz zlib-dev libpng-dev libxml2-dev  zip libzip-dev \
        && docker-php-ext-configure zip \ 
        && docker-php-ext-install gd pdo_mysql soap mysqli zip
EOF
docker build -t  php-fpm:7.4 .
docker images
sudo docker save -o /opt/images/php-fpm-7.4.28.tar php-fpm:7.4

配置 LNMP 环境

 sudo mkdir /opt/lnmp
sudo chown docker /opt/lnmp
cat > /opt/lnmp/docker-compose.yml << EOF
version: '2.1'
services:
  db:
    image: mariadb:10.5
    container_name: mysql
    ports:
      - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=password                   # Root password of MySQL -- must be changed
      - MYSQL_LOG_CONSOLE=true
    volumes:
      - /opt/lnmp/mysql-data:/var/lib/mysql         # Volume of MySQL (directory for persistent storage) and mount point in container -- can be changed (not advised)
    networks:
      lnmp-net:
        ipv4_address: 172.18.0.3

  nginx:
    image: nginx
    container_name: nginx
    ports:
      - "80:80"
    volumes:
      - /opt/lnmp/nginx/www:/usr/share/nginx/html
      - /opt/lnmp/nginx/conf:/etc/nginx/conf
      - /opt/lnmp/nginx/conf.d:/etc/nginx/conf.d
      - /opt/lnmp/nginx/logs:/var/log/nginx

    networks:       lnmp-net:
        ipv4_address: 172.18.0.2

   php:
    image: php-fpm:7.4
    container_name: php
    volumes:
      - /opt/lnmp/nginx/www:/www
    networks:       
        lnmp-net:
        ipv4_address: 172.18.0.3

 networks:
   ipam:
     config:
        - subnet: "172.18.0.0/24"
 EOF

cat > /opt/lnmp/nginx/conf.d/php.conf << EOF
server {
        listen  80;
        server_name     localhost;
    client_max_body_size 1000m;

        location / {
                root    /usr/share/nginx/html;
                index   index.html index.htm index.php;
                if ( !-f $request_filename  ) {
                        rewrite ^/login$ /login.php last;
                        rewrite ^/register$ /register.php last;
                        rewrite ^/search$ /search.php last;
                        rewrite ^/discount/student$ /discount.php?u=d last;
                        rewrite ^/discount/teacher$ /discount.php?u=t last;
                        # rewrite ^/admin.php$ /404.html break;
                         rewrite ^/(.*)$ /index.php/$1 last;
                }
        }
        error_page      500 502 503 504 /50x.html;
        location = /50x.html {
                root    /usr/share/nginx/html;
        }

        location ~ \.php(.*)$ {
                root /www;
                fastcgi_buffer_size 128k;
                fastcgi_buffers 4 256k;
                fastcgi_busy_buffers_size 256k;
                fastcgi_pass    php:9000;
                fastcgi_index   index.php;
                fastcgi_split_path_info ^(.+\.php)(.*)$;
                fastcgi_param   SCRIPT_FILENAME /www$fastcgi_script_name;
                fastcgi_param   PATH_INFO       $fastcgi_path_info;
                include         fastcgi_params;
        }
}
EOF
cat > /opt/lnmp/nginx/www/index.php << EOF
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
echo '<h1 style="text-align: center;">欢迎使用DNMP!</h1>';
echo '<h2>版本信息</h2>';
echo '<ul>';
echo '<li>PHP版本:', PHP_VERSION, '</li>';
echo '<li>Nginx版本:', $_SERVER['SERVER_SOFTWARE'], '</li>';
echo '<li>MySQL服务器版本:', getMysqlVersion(), '</li>';
echo '<li>Redis服务器版本:', getRedisVersion(), '</li>';
echo '<li>MongoDB服务器版本:', getMongoVersion(), '</li>';
echo '</ul>';
echo '<h2>已安装扩展</h2>';
printExtensions();
/**
        *  * 获取MySQL版本
        *   */
function getMysqlVersion()
{
    if (extension_loaded('PDO_MYSQL')) {
        try {
            $dbh = new PDO('mysql:host=mysql;dbname=mysql', 'root', '123456');
            $sth = $dbh->query('SELECT VERSION() as version');
            $info = $sth->fetch();
          } catch (PDOException $e) {
                return $e->getMessage();
          }
          return $info['version'];
      } else {
       return 'PDO_MYSQL 扩展未安装 ×';
  }
}

/**
        *  * 获取Redis版本
        *   */
function getRedisVersion()
{
    if (extension_loaded('redis')) {
        try {
            $redis = new Redis();
            $redis->connect('redis', 6379);
            $info = $redis->info();
            return $info['redis_version'];
          } catch (Exception $e) {
               return $e->getMessage();
        }
     } else {
      return 'Redis 扩展未安装 ×';
  }
}

/**
 *  * 获取MongoDB版本
 *   */
function getMongoVersion()
{
    if (extension_loaded('mongodb')) {
            try {
                $manager = new MongoDB\Driver\Manager('mongodb://root:123456@mongodb:27017');
                $command = new MongoDB\Driver\Command(array('serverStatus'=>true));
                $cursor = $manager->executeCommand('admin', $command);
                return $cursor->toArray()[0]->version;
            } catch (Exception $e) {
                return $e->getMessage();
                }
        } else {
        return 'MongoDB 扩展未安装 ×';
    }
}

/**
 *  * 获取已安装扩展列表
 *   */
function printExtensions()
{
    echo '<ol>';
    foreach (get_loaded_extensions() as $i => $name) {
        echo "<li>", $name, '=', phpversion($name), '</li>';
    }
    }
    echo '</ol>';
}
EOF
cd /opt/lnmp
docker-compose up                

 

安装扩展

默认php镜像中提供的扩展比较少,缺少诸如mysql、gd2等常用的扩展,这样我们就需要自己安装并启用扩展。

首先进入到php容器,用php -m命令查看本地有什么扩展。

可以使用docker-php-ext-install命令来安装扩展。

docker-php-ext-install mysql

扩展安装好之后就可以在php.ini中启用。我们从phpinfo中可以看到,容器环境下默认的php.ini没有启用,可以从/usr/local/etc/php下将php.ini-development拷贝为php.ini。通过修改php.ini中配置,启用自己需要的扩展。

下面是几个扩展安装的命令,供参考。

  • docker-php-ext-source 在容器中创建一个/usr/src/php目录
  • docker-php-ext-enable启用PHP扩展,省去我们手工编辑php.ini的过程
  • docker-php-ext-install安装并启用PHP扩展
  • docker-php-ext-configure经常与docker-php-ext-install搭配,在需要自定义扩展的配置时使用

单独运行命令行

如果仅需要在命令行下运行 php 命令,可以使用如下方式

# 将当前目录挂载到 /tmp 目录,执行 php 命令,结束后退出

docker run -it --rm -v $PWD:/tmp -w /tmp php-fpm:7.4 php test.php

备份和恢复数据库

docker exec -it seafile-mysql mysqldump  -uroot --opt ccnet_db > ccnet_db.sql
docker exec -it seafile-mysql mysqldump  -uroot --opt seafile_db > seafile_db.sql
docker exec -it seafile-mysql mysqldump  -uroot --opt seahub_db > seahub_db.sql

docker cp /opt/seafile-backup/databases/ccnet_db.sql seafile-mysql:/tmp/ccnet_db.sql
docker cp /opt/seafile-backup/databases/seafile_db.sql seafile-mysql:/tmp/seafile_db.sql
docker cp /opt/seafile-backup/databases/seahub_db.sql seafile-mysql:/tmp/seahub_db.sql

docker exec -it seafile-mysql /bin/sh -c "mysql -uroot ccnet_db < /tmp/ccnet_db.sql"
docker exec -it seafile-mysql /bin/sh -c "mysql -uroot seafile_db < /tmp/seafile_db.sql"
docker exec -it seafile-mysql /bin/sh -c "mysql -uroot seahub_db < /tmp/seahub_db.sql"

常见问题

mysql 容器

解决方法:

security_opt:
  - seccomp:unconfined

参考内容

配置指令

  • ARG : 定义创建镜像过程中使用的变量,格式为 ARG <name>[=<default value>]
  • FROM : 指定所创建镜像的基础镜像。格式为 FROM <image>:<tag>
  • LABEL : 为生成的镜像添加元数据标签信息,辅助过滤特定镜像。格式为 LABEL <key>=<value> <key>=<value>
  • EXPOSE : 声明镜像内服务监听的端口。格式为 EXPOSE <port>[/<protocol>]
  • ENV : 指定环境变量,该变量在容器中存在,也可在容器启动时覆盖。格式为 ENV <key> <value>
  • ENTRYPOINT : 指定镜像的默认入口命令,做为容器启动时的根命令执行。格式为 ENTRYPOINT ["executable", "param1", "param2"] 或者 ENTRYPOINT command param1 param2
  • VOLUME : 创建一个数据卷挂载点。格式为 VOLUME ["/data"]
  • USER : 指定容器运行时的用户名或UID,后续的RUN指令也使用该用户身份。格式为 USER daemon
  • WORKDIR : 配置RUN\CMD\ENTRYPOINT等指令的工作目录,推荐使用绝对路径。格式为:WORKDIR /path/to/workdir
  • ONBUILD : 指定当基于所生成镜像创建子镜像时,自动执行的操作指令。
  • STOPSIGNAL : 指定容器接收退出的信号值。格式为: STOPSIGNAL signal
  • HEALTHCHECK : 配置容器健康检查命令,自 Docker 1.12 开始支持。格式为: HEALTHCHECK [OPTIONS] CMD command
  • SHELL : 指定默认的shell类型。格式为: SHELL ["executable", "parameters"]

操作指令

  • RUN : 运行指定命令。格式为: RUN <command> 或 RUN ["executable", "param1", "param2"] 当命令较长时,可以用 \ 来换行。
  • CMD : 指定容器启动时默认执行的命令,每个Dockerfile只能有一条CMD命令。格式有三种,分别为:CMD ["executable", "param1", "param2"] 或 CMD command param1 param2 或 CMD ["param1", "param2"]
  • ADD : 添加内容到镜像中,将SRC内容复制到DEST中。格式为: ADD <src> <dest>
  • COPY : 复制内容到镜像中。格式为 : COPY <src> <dest>

  • 无标签
写评论...