dockerhub中的镜像的制作过程?

最近公司在搞docket容器化部署,对镜像的制作过程比较感兴趣,因为我现在用的镜像都是现成的基础镜像在上面添砖加瓦,我看官网的镜像如mysql redis tomcat都有专门的dockerfile以及各种介质文件,entrypoint.sh,看了一下没看懂太多,请问有比较详细的介绍如何在dockerfile仿照官方镜从零开始像制作镜像的教程吗?

譬如给一个nginx的镜像如何快速的获取它的内置的环境变量,可以设置到启动命令中的env,以及解读entrypoint.sh中不同的参数有什么效果。

阅读 646
1 个回答

本质上其实就是写启动脚本(ENTRYPOINT),脚本里可能做一些初始化+启动的工作。

先举个redis的例子,首先redis的dockerfile里面指定了 ENTRYPOINT,也就是容器启动时默认的执行命令。

Dockerfile

...
COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["docker-entrypoint.sh"]

EXPOSE 6379
CMD ["redis-server"]

docker-entrypoint.sh

#!/bin/sh
set -e

# first arg is `-f` or `--some-option`
# or first arg is `something.conf`
if [ "${1#-}" != "$1" ] || [ "${1%.conf}" != "$1" ]; then
        set -- redis-server "$@"
fi

# allow the container to be started with `--user`
if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then
        find . \! -user redis -exec chown redis '{}' +
        exec gosu redis "$0" "$@"
fi

# set an appropriate umask (if one isn't set already)
# - https://github.com/docker-library/redis/issues/305
# - https://github.com/redis/redis/blob/bb875603fb7ff3f9d19aad906bd45d7db98d9a39/utils/systemd-redis_server.service#L37
um="$(umask)"
if [ "$um" = '0022' ]; then
        umask 0077
fi

exec "$@"

再看docker hub 上关于redis的使用说明:

start a redis instance
$ docker run --name some-redis -d redis

这里启动一个redis实例,没有传任何参数,直接启动容器。所以docker会首先执行ENTRYPOINT的命令,并给ENTRYPOINT添加Dockerfile里面的CMD参数。
最终这个容器执行的命令就是 docker-entrypoint.sh redis-server
然后再看docker-entrypoing.sh脚本的内容。
第一个if块,判断CMD参数是不是-开头或conf结尾的参数,所以跳过
第二个if快,判断有没有指定用户,跳过
第三个if快,修改默认权限
最后exec $@启动redis-server
所以在没有加参数的时候,直接运行了redis-server。

第二个使用说明:

start with persistent storage
$ docker run --name some-redis -d redis redis-server --save 60 1 --loglevel warning

这里手动指定了redis-server --save 60 1 --loglevel warning,添加了参数,这些参数会覆盖Dockerfile里面的CMD指令。
当这个容器运行的时候,仍然会去执行docker-entrypoint.sh,两个if还是没有匹配到,还是直接到exec $@, 运行的命令就是 redis-server --save 60 1 --loglevel warning


下面再来看一个相对比较复杂的MySQL

$ docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci

这里使用-e指定环境变量(MYSQL_ROOT_PASSWORD)和添加CMD参数(--character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci)

根据Dockerfile的内容,运行容器的时候,可以看到也是调用了 entrypoint.sh 文件。

这里直接看entrypoint.sh 的内容,直接跳到_main()函数,看逻辑还是比较清晰的。也有注释。

_main() {
        # if command starts with an option, prepend mysqld
        if [ "${1:0:1}" = '-' ]; then
                set -- mysqld "$@"
        fi

        # skip setup if they aren't running mysqld or want an option that stops mysqld
        if [ "$1" = 'mysqld' ] && ! _mysql_want_help "$@"; then
                mysql_note "Entrypoint script for MySQL Server ${MYSQL_VERSION} started."

                mysql_check_config "$@"
                # Load various environment variables
                docker_setup_env "$@"
                docker_create_db_directories

                # If container is started as root user, restart as dedicated mysql user
                if [ "$(id -u)" = "0" ]; then
                        mysql_note "Switching to dedicated user 'mysql'"
                        exec gosu mysql "$BASH_SOURCE" "$@"
                fi

                # there's no database, so it needs to be initialized
                if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
                        docker_verify_minimum_env

                        # check dir permissions to reduce likelihood of half-initialized database
                        ls /docker-entrypoint-initdb.d/ > /dev/null

                        docker_init_database_dir "$@"

                        mysql_note "Starting temporary server"
                        docker_temp_server_start "$@"
                        mysql_note "Temporary server started."

                        docker_setup_db
                        docker_process_init_files /docker-entrypoint-initdb.d/*

                        mysql_expire_root_user

                        mysql_note "Stopping temporary server"
                        docker_temp_server_stop
                        mysql_note "Temporary server stopped"

                        echo
                        mysql_note "MySQL init process done. Ready for start up."
                        echo
                fi
        fi
        exec "$@"
}

读取环境变量这一步在 docker_setup_db 函数,最后如果要自己实现类似的功能,可以参考他是怎么处理的。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
logo
Stack Overflow 翻译
子站问答
访问
宣传栏