本章目录
1. 使用 Dockerfile 定制镜像
2. Dockerfile 指令详解
2.1 FROM:指定基础镜像
2.2 RUN:执行命令
2.3 COPY:复制文件
2.4 ADD:更高级的复制文件
2.5 CMD:容器启动命令
2.6 ENTRYPOINT:指定运行时要运行的命令
2.7 ENV:设置环境变量
2.8 ARG:构建参数
2.9 VOLUME:定义数据卷目录
2.10 EXPOSE:声明端口
2.11 WORKDIR:指定工作目录
2.12 USER:指定当前用户
2.13 HEALTHCHECK:健康检查
2.14 ONBUILD 命令
3. 镜像构建实例
3.1 Nginx 镜像
3.2 Tomcat 镜像
3.3 SSHD 镜像
3.4 Systemctl 镜像
3.5 MySQL 镜像
镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。
Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
此处以定制 nginx 镜像为例,使用 Dockerfile 来定制。
在一个空白目录中,建立一个文本文件,并命名为 Dockerfile:
1 mkdir mynginx 2 cd mynginx 3 touch Dockerfile
其内容为:
1 FROM nginx 2 RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
这个 Dockerfile 很简单,一共就两行。涉及到了两条指令, FROM 和 RUN。
在编写 Dockerfile 时,有严格的格式需要遵循:
- 第一行必须使用 FROM 指令指明所基于的镜像名称
- 之后使用 MAINTAINER 指令说明维护该镜像的用户信息
- 然后是镜像操作相关指令,如RUN指令;每运行一条指令,都会给基础镜像添加新的一层
- 最后使用CMD指令指定启动容器时要运行的命令操作
2. Dockerfile 指令详解
2.1 FROM:指定基础镜像
所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。而 FROM 就是指定基础镜像,因此一个 Dockerfile 中 FROM 是必备的指令,并且必须是第一条指令。
除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch 。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。
如果你以 scratch 为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。
不以任何系统为基础,直接将可执行文件复制进镜像的做法并不罕见,比如 swarm 、 coreos/etcd 。
对于 Linux 下静态编译的程序来说,并不需要有操作系统提供运行时支持,所需的一切库都已经在可执行文件里了,因此直接 FROM scratch 会让镜像体积更加小巧。使用 Go 语言 开发的应用很多会使用这种方式来制作镜像,这也是为什么有人认为 Go 是特别适合容器微服务架构的语言的原因之一。
RUN 指令是用来执行命令行命令的。由于命令行的强大能力, RUN 指令在定制镜像时是最常用的指令之一。其格式有两种:
- shell 格式: RUN <命令> ,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的 RUN 指令就是这种格式。
1 RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
- exec 格式: RUN ["可执行文件", "参数1", "参数2"],这更像是函数调用中的格式。
Dockerfile 中每一个指令都会建立一层, RUN 也不例外。
每一个 RUN 的行为,就和刚才我们手工建立镜像的过程一样:新建立一层,在其上执行这些命令,执行结束后, commit 这一层的修改,构成新的镜像。
这是完全没有意义的,而且很多运行时不需要的东西,都被装进了镜像里,比如编译环境、更新的软件包等等。
结果就是产生非常臃肿、非常多层的镜像,不仅仅增加了构建部署的时间,也很容易出错。
Union FS 是有最大层数限制的,比如 AUFS,曾经是最大不得超过 42 层,现在是不得超过127 层。
所以 Dockerfile 正确的写法应该是这样(例):
1 FROM debian:jessie 2 RUN buildDeps='gcc libc6-dev make' \ 3 && apt-get update \ 4 && apt-get install -y $buildDeps \ 5 && wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \ 6 && mkdir -p /usr/src/redis \ 7 && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \ 8 && make -C /usr/src/redis \ 9 && make -C /usr/src/redis install \ 10 && rm -rf /var/lib/apt/lists/* \ 11 && rm redis.tar.gz \ 12 && rm -r /usr/src/redis \ 13 && apt-get purge -y --auto-remove $buildDeps
镜像是多层存储,每一层的东西并不会在下一层被删除,会一直跟随着镜像。
因此镜像构建时,一定要确保每一层只添加真正需要添加的东西,任何无关的东西都应该清理掉。
- 镜像构建上下文(Context)
虽然表面上我们好像是在本机执行各种 docker 功能,但实际上,一切都是使用远程调用形式在服务端(Docker 引擎)完成。
当我们进行镜像构建的时候,并非所有定制都会通过 RUN 指令完成,经常会需要将一些本地文件复制进镜像,比如通过 COPY 指令、 ADD 指令等。而 docker build 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。
当构建的时候,用户会指定构建镜像上下文的路径, docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。
一般来说,应该会将 Dockerfile 置于一个空目录下,或者项目根目录下。如果该目录下没有所需文件,那么应该把所需文件复制一份过来。
如果目录下有些东西确实不希望构建时传给 Docker 引擎,那么可以用 .gitignore 一样的语法写一个 .dockerignore ,该文件是用于剔除不需要作为上下文传递给 Docker 引擎的。
在默认情况下,如果不额外指定 Dockerfile 的话,会将上下文目录下的名为 Dockerfile 的文件作为 Dockerfile。
这只是默认行为,实际上 Dockerfile 的文件名并不要求必须为 Dockerfile ,而且并不要求必须位于上下文目录中,比如可以用 -f ../Dockerfile.php 参数指定某个文件作为 Dockerfile 。
格式:
- COPY <源路径>... <目标路径>
- COPY ["<源路径1>",... "<目标路径>"]
和 RUN 指令一样,也有两种格式,一种类似于命令行,一种类似于函数调用。COPY 指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置。比如:
1 COPY package.json /usr/src/app/
<源路径> 可以是多个,甚至可以是通配符,其通配符规则要满足 Go 的 filepath.Match 规则,如:
1 COPY hom* /mydir/ 2 COPY hom?.txt /mydir/
<目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR 指令来指定)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。
此外,还需要注意一点,使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用。特别是构建相关文件都在使用 Git 进行管理的时候。
- 设定容器启动时第一个运行的命令及其参数。
- 可以通过 使用命令 docker run --entrypoint 来覆盖镜像中的 ENTRYPOINT 指令的内容。
- 将源文件复制到镜像中,源文件要与Dockerfile 位于相同目录中,或者是一个URL。
有如下注意事项:
- 如果源路径是个文件,且目标路径是以 / 结尾, 则 docker 会把目标路径当作一个目录,会把源文件拷贝到该目录下。
- 如果目标路径不存在,则会自动创建目标路径。
- 如果源路径是个文件,且目标路径是不是以 / 结尾,则 docker 会把目标路径当作一个文件。
- 如果目标路径不存在,会以目标路径为名创建一个文件,内容同源文件。
- 如果目标文件是个存在的文件,会用源文件覆盖它,当然只是内容覆盖,文件名还是目标文件名。
- 如果目标文件实际是个存在的目录,则会源文件拷贝到该目录下。注意,这种情况下,最好显示的以 / 结尾,以避免混淆。
- 如果源路径是个目录,且目标路径不存在,则 docker 会自动以目标路径创建一个月录,把源路径月录下的文件拷贝进来。
- 如果目标路径是个已经存在的目录,则 docker 会把源路径目录下的文件拷贝到该目录下。
- 如果源文件是个归档文件(压缩文件),则 docker会自动帮解压。
URL 下载和解压特性不能一起使用。任何压缩文件通过 URL 拷贝,都不会自动解压。
ADD 指令和 COPY 的格式和性质基本一致。但是在 COPY 基础上增加了一些功能。比如 <源路径> 可以是一个 URL ,这种情况下,Docker 引擎会试图去下载这个链接的文件放到 <目标路径> 去。
下载后的文件权限自动设置为 600 ,如果这并不是想要的权限,那么还需要增加额外的一层 RUN 进行权限调整,另外,如果下载的是个压缩包,需要解压缩,也一样还需要额外的一层 RUN 指令进行解压缩。所以不如直接使用 RUN 指令,然后使用 wget 或者 curl 工具下载,处理权限、解压缩、然后清理无用文件更合理。因此,这个功能其实并不实用,而且不推荐使用。
如果 <源路径> 为一个 tar 压缩文件的话,压缩格式为 gzip , bzip2 以及 xz 的情况下, ADD 指令将会自动解压缩这个压缩文件到 <目标路径> 去。
但在某些情况下,如果我们真的是希望复制个压缩文件进去,而不解压缩,这时就不可以使用 ADD 命令了。
在 Docker 官方的 Dockerfile 最佳实践文档 中要求,尽可能的使用 COPY ,因为 COPY 的语义很明确,就是复制文件而已,而 ADD 则包含了更复杂的功能,其行为也不一定很清晰。最适合使用 ADD 的场合,就是所提及的需要自动解压缩的场合。
另外需要注意的是, ADD 指令会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。
因此在 COPY 和 ADD 指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用 COPY 指令,仅在需要自动解压缩的场合使用 ADD 。
CMD 指令的格式和 RUN 相似,也是两种格式:
- shell 格式: CMD <命令> 参数1 参数2
- exec 格式: CMD ["可执行文件", "参数1", "参数2"...]
- 参数列表格式: CMD ["参数1", "参数2"...] 。
在指定了 ENTRYPOINT 指令后,用 CMD 指定具体的参数。
Docker 不是虚拟机,容器就是进程。既然是进程,那么在启动容器的时候,需要指定所运行的程序。
启动容器时默认执行的命令或者脚本,Dockerfile只 能有一条CMD命令。如果指定多条命令,只执行最后一条命令。
如果在 docker run 时指定了命令或者镜像中有 ENTRYPOINT,那么 cmd 就会被覆盖。
- 格式:ENTRYPOINT [“要运行的程序”,“参数1”,“参数2”]
设定容器启动时第一个运行的命令及其参数。
可以通过使用命令 docker run --entrypoint 来覆盖镜像中的 ENTRYPOINT 指令的内容。
格式有两种:
- ENV <key> <value>
- ENV <key1>=<value1> <key2>=<value2>...
设置一个环境变量的值,会被后面的RUN使用。
下列指令可以支持环境变量展开:
- ADD 、 COPY 、 ENV 、 EXPOSE 、 LABEL 、 USER 、 WORKDIR 、 VOLUME 、 STOPSIGNAL 、 ONBUILD 。
可以从这个指令列表里感觉到,环境变量可以使用的地方很多,很强大。通过环境变量,我们可以让一份 Dockerfile 制作更多的镜像,只需使用不同的环境变量即可。
- 格式: ARG <参数名>[=<默认值>]
构建参数和 ENV 的效果一样,都是设置环境变量。所不同的是, ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。但是不要因此就使用 ARG 保存密码之类的信息,因为 docker history 还是可以看到所有值的。
Dockerfile 中的 ARG 指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令 docker build 中用 --build-arg <参数名>=<值> 来覆盖。
在 1.13 之前的版本,要求 –build-arg 中的参数名,必须在 Dockerfile 中用 ARG 定义过了,换句话说,就是 –build-arg 指定的参数,必须在 Dockerfile 中使用了。如果对应参数没有被使用,则会报错退出构建。从 1.13 开始,这种严格的限制被放开,不再报错退出,而是显示警告信息,并继续构建。
在容器中创建一个挂载点。
格式为:
- VOLUME ["<路径1>", "<路径2>"...]
- VOLUME <路径>
容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中。为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在 Dockerfile 中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。
指定新镜像加载到 Docker 时要开启的端口。
- 格式:EXPOSE <端口1> [<端口2>...]
EXPOSE 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
为后续的 RUN、 CMD、 ENTRYPOINT 指定工作目录。
- 格式:WORKDIR <工作目录路径>
使用 WORKDIR 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在, WORKDIR 会帮你建立目录。
指定运行容器时的用户。
- 格式: USER <用户名/UID>
USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层。 WORKDIR 是改变工作目录, USER 则是改变之后层的执行 RUN , CMD 以及 ENTRYPOINT 这类命令的身份。
和 WORKDIR 一样, USER 只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。
格式:
- HEALTHCHECK [选项] CMD <命令> :设置检查容器健康状况的命令
- HEALTHCHECK NONE :如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
HEALTHCHECK 指令是告诉 Docker 应该如何进行判断容器的状态是否正常。
HEALTHCHECK 支持下列选项:
- interval=<间隔> :两次健康检查的间隔,默认为 30 秒;
- timeout=<时长> :健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒;
- retries=<次数> :当连续失败指定次数后,则将容器状态视为 unhealthy ,默认 3 次。
和 CMD , ENTRYPOINT 一样, HEALTHCHECK 只可以出现一次,如果写了多个,只有最后一个生效。
在 HEALTHCHECK [选项] CMD 后面的命令,格式和 ENTRYPOINT 一样,分为 shell 格式,和 exec 格式。
命令的返回值决定了该次健康检查的成功与否: 0 :成功; 1 :失败; 2 :保留,不要使用这个值。
指定所生成的镜像作为一个基础镜像时所要运行的命令。
ONBUILD 是一个特殊的指令,它后面跟的是其它指令,比如 RUN , COPY 等,而这些指令,在当前镜像构建时并不会被执行。
只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。
- 格式: ONBUILD <其它指令>
当在一个 Dockerfile 文件中加上 ONBUILD 指令, 该指令对利用该 Dockerfile 构建镜像(比如为 A 镜像)不会产生实质性影响。
但是当编写一个新的 Dockerfile 文件来基于A镜像构建一个镜像(比如为 B 镜像)时,这时构造 A 镜像的 Dockerfile 文件中的 ONBUILD 指令就生效了,在构建 B 镜像的过程中,首先会执行 ONBUILD 指令指定的指令,然后才会执行其它指令。
1 mkdir /root/nginx 2 cd /root/nginx #拉入软件包(nginx-1.12.0.tar.gz) 3 4 vim Dockerfile 5 6 FROM centos:7 7 MAINTAINER wt 8 RUN yum -y update 9 RUN yum -y install pcre-devel zlib-devel gcc gcc-c++ make 10 RUN useradd -M -s /sbin/nologin nginx 11 12 ADD nginx-1.12.0.tar.gz /usr/local/src 13 WORKDIR /usr/local/src/nginx-1.12.0 14 RUN ./configure \ 15 --prefix=/usr/local/nginx \ 16 --user=nginx \ 17 --group=nginx \ 18 --with-http_stub_status_module && make && make install 19 20 ENV PATH /usr/local/nginx/sbin:$PATH 21 22 EXPOSE 80 23 EXPOSE 443 24 RUN echo "daemon off;" >> /usr/local/nginx/conf/nginx.conf 25 26 CMD ["/usr/local/nginx/sbin/nginx"]
Dockerfile 文件里有几个 ADD 就说明需要几个文件 ( Dockerfile 文件不包含在内)
1 ##生成镜像 2 3 docker build -t nginx:nginxweb . 4 5 6 ##启动容器 7 8 docker run -d -P nginx:nginxweb
1 mkdir tomcat 2 cd tomcat/ #拉入软件包(jdk-8u201-linux-x64.rpm apache-tomcat-9.0.16.tar.gz) 3 4 vim Dockerfile 5 6 FROM centos:7 7 MAINTAINER wt 8 ADD jdk-8u201-linux-x64.rpm /usr/local/ 9 10 WORKDIR /usr/local/ 11 RUN rpm -ivh jdk-8u201-linux-x64.rpm 12 ENV JAVA_HOME=/usr/java/jdk1.8.0_201-amd64 13 ENV CLASSPATH=.:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar 14 ENV PATH=$JAVA_HOME/bin:$PATH 15 16 ADD apache-tomcat-9.0.16.tar.gz /usr/local/ 17 WORKDIR /usr/local/ 18 RUN mv apache-tomcat-9.0.16 /usr/local/tomcat 19 20 EXPOSE 8080 21 22 CMD ["/usr/local/tomcat/bin/catalina.sh","run"] 23 #ENTRYPOINT ["/usr/local/tomcat/bin/catalina.sh","run"]
1 ##生成镜像 2 3 docker build -t tomcat:tomcatweb . 4 5 ##启动容器 6 7 docker run -d --name tomcatweb -p 1234:8080 tomcat:tomcatweb
1 mkdir sshd 2 cd sshd/ 3 4 vim Dockerfile 5 6 FROM centos:7 7 MAINTAINER wt 8 RUN yum -y install openssh* net-tools lsof telnet passwd 9 RUN echo "123456" | passwd --stdin root 10 RUN sed -i 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_config 11 RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key 12 RUN sed -i '/^session\s\+required\s\+pam_loginuid.so/s/^/#/' /etc/pam.d/sshd 13 RUN mkdir -p /root/.ssh && chown root:root /root && chmod 700 /root/.ssh 14 EXPOSE 22 15 CMD ["/usr/sbin/sshd","-D"]
1 ##生成镜像 2 3 docker build -t sshd:test . 4 5 ##启动容器 6 7 docker run -d -P sshd:test 8 ssh localhost -p 端口号
1 mkdir systemctl 2 cd systemctl 3 4 vim Dockerfile 5 6 FROM sshd:test 7 MAINTAINER wt 8 ENV container docker 9 RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *;do [ $i== \ 10 systemd-tmpfiles-setup.service ] || rm -f $i;done);\ 11 rm -f /lib/systemd/system/multi-user.target.wants/*;\ 12 rm -f /etc/systemd/system/*.wants/*;\ 13 rm -f /lib/systemd/system/local-fs.target.wants/*;\ 14 rm -f /lib/systemd/system/sockets.target.wants/*udev*;\ 15 rm -f /lib/systemd/system/sockets.target.wants/*initctl*;\ 16 rm -f /lib/systemd/system/basic.target.wants/*;\ 17 rm -f /lib/systemd/system/anaconda.target.wants/*; 18 VOLUME ["/sys/fs/cgroup"] 19 CMD ["/usr/sbin/init"]
1 ##生成镜像 2 3 docker build -t systemctl:systemctl . 4 5 ##启动容器 6 7 docker run --privateged -it -v /sys/fs/cgroup:/sys/fs/cgroup:ro systemd:systemctl /sbin/init & 8 9 ##登录容器进行测试 10 11 docker exec -it systemctl /bin/bash 12 13 systemctl status sshd 14 systemctl start sshd 15 systemctl status sshd
1 mkdir mysql 2 cd mysql/ 3 4 vim Dockerfile 5 6 FROM centos:7 7 MAINTAINER wt 8 EXPOSE 3306 9 10 ADD mysql-boost-5.7.20.tar.gz /usr/local/src 11 WORKDIR /usr/local/src/mysql-5.7.20 12 RUN useradd mysql -M -s /sbin/nologin 13 RUN yum -y install gcc \ 14 gcc-c++ \ 15 make \ 16 ncurses \ 17 ncurses-devel \ 18 bison \ 19 cmake 20 RUN cmake \ 21 -DCMAKE_INSTALL_PREFIX=/usr/local/mysql \ 22 -DMYSQL_UNIX_ADDR=/usr/local/mysql/mysql.sock \ 23 -DSYSCONFDIR=/etc \ 24 -DSYSTEMD_PID_DIR=/usr/local/mysql \ 25 -DDEFAULT_CHARSET=utf8 \ 26 -DDEFAULT_COLLATION=utf8_general_ci \ 27 -DWITH_INNOBASE_STORAGE_ENGINE=1 \ 28 -DWITH_ARCHIVE_STORAGE_ENGINE=1 \ 29 -DWITH_BLACKHOLE_STORAGE_ENGINE=1 \ 30 -DWITH_PERFSCHEMA_STORAGE_ENGINE=1 \ 31 -DMYSQL_DATADIR=/usr/local/mysql/data \ 32 -DWITH_BOOST=boost \ 33 -DWITH_SYSTEMD=1 34 RUN make -j 4 && make install 35 RUN chown -R mysql:mysql /usr/local/mysql/ 36 RUN rm -rf /etc/my.cnf 37 WORKDIR /etc/ 38 ADD my.cnf /etc/ 39 40 41 RUN chown mysql:mysql /etc/my.cnf 42 43 ENV PATH $PATH:/usr/local/mysql/bin:/usr/local/mysql/lib 44 RUN /usr/local/mysql/bin/mysqld \ 45 --initialize-insecure \ 46 --user=mysql \ 47 --basedir=/usr/local/mysql \ 48 --datadir=/usr/local/mysql/data 49 50 RUN cp /usr/local/mysql/usr/lib/systemd/system/mysqld.service /lib/systemd/system/ 51 RUN echo -e "#!/bin/bash \nsystemctl enable mysqld" > /run.sh 52 RUN chmod 755 /run.sh 53 RUN sh /run.sh 54 55 CMD ["init"]
1 ##配置文件 2 3 4 vim /mysql/my.cnf 5 6 [client] 7 port = 3306 8 default-character-set=utf8 9 socket = /usr/local/mysql/mysql.sock 10 11 [mysql] 12 port = 3306 13 default-character-set=utf8 14 socket = /usr/local/mysql/mysql.sock 15 16 [mysqld] 17 user = mysql 18 basedir = /usr/local/mysql 19 datadir = /usr/local/mysql/data 20 port = 3306 21 character_set_server=utf8 22 pid-file = /usr/local/mysql/mysqld.pid 23 socket = /usr/local/mysql/mysql.sock 24 server-id = 1 25 26 sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_AUTO_VALUE_ON_ZERO,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,PIPES_AS_CONCAT,ANSI_QUOTES
1 ##运行脚本 2 3 vim run.sh 4 5 #!/bin/bash 6 /usr/local/mysql/bin/mysqld 7 8 9 docker build -t mysql:mysql00 .
1 ##创建容器 2 3 docker run -d --name mysql -P --privileged mysql:mysql00 4 5 ##启动容器 6 7 docker start 容器 id 8 9 ##进入容器授权 10 11 docker exec -it 容器id /bin/bash 12 13 mysql -u root -p #登录MySQL 14 15 grant all privileges on *.* to 'root'@'%' identified by '123456'; 16 grant all privileges on *.* to 'root'@'localhost' identified by '123456'; 17 18 flush privileges; 19 20 exit
1 ##在宿主机 yum 安装 MySQL 2 3 yum -y install mysql 4 5 ##客户端连接 MySQL 容器 6 7 mysql -uroot -p123456 -h 192.168.153.40 -P3306
-