赞
踩
镜像就像操作系统。容器就像装了操作系统的虚拟机。(这种说法不完全正确,容器和虚拟机不一样,但可以这么理解。)
docker search centos
docker search ubuntu
docker pull ubuntu:16.04
docker pull ubuntu # 不加版本号默认下载最新版
docker pull centos:7
docker pull centos # 不加版本号默认下载最新版
附:查看docker支持的镜像网站:
docker images
比如我们将上面下载的最新的centos镜像标注以下,以方面区分。
以上图为例,将镜像名为centos、版本号 latest的镜像起别名为:local_centos、版本号为v1:
# 法1: repository名法。(注意 repository名 可能会有重名情况)
docker tag centos local_centos:v1
# 法2: image法(iamge_id 除了别名以外具有唯一性)
docker tag 5d0da3dc9764 local_centos:v1
# 法3(推荐):repository名+TAG法。(具有唯一性)
docker tag centos:latest local_centos:v1
再次输入 docker images
查看:
上面推荐使用法3,方便快捷,可以按Tab
键自动生成补全信息;而法2虽然具有唯一性,但不会自动补全,反而输入IMAGE_ID很麻烦;法1不具备唯一性,当有重名的时候不适用,如下,我们又下载了一个centos7的镜像时:
这里centos重名,法1不再适用。只能选具备唯一性的法2或法3。
# 法1:打包 iamge_id 为 eeb6ee3f44bd 的镜像,保存到 /opt/centos7.tar.gz
docker save -o /opt/centos7.tar.gz eeb6ee3f44bd
# 法2:打包 repository名 为 centos、版本号为7的镜像。(注意 repository名 可能会有重名)
docker save -o /opt/centos7.tar.gz centos:7
# 打包 repository名为 local_centos镜像。(注意 repository名 可能会有重名)
docker save -o /opt/local_centos.tar.gz local_centos
注:参数 -o
即 output
提示:如果为非root用户,操作 /opt目录 需要sudo权限。
# 法1(image_id法)
docker rmi eeb6ee3f44bd
# 法2(repository + TAG 法)
docker rmi centos:7
注:参数 rmi 即 rm image
最新版centos镜像有两个:centos(latest)和它的别名 local_centos(v1)
如果想这两个都删除,有两种方式:
docker rmi centos:latest
docker rmi local_centos:v1
这两个image_id相同的(为5d0da3dc9764)镜像,当输入
docker rmi 5d0da3dc9764
会报错:
Error response from daemon: conflict: unable to delete 5d0da3dc9764 (must be forced) - image is referenced in multiple repositories
需要加上强制参数-f
。
docker rmi -f 5d0da3dc9764
注:参数 -f 即force,类似的还有 rm -rf
如将之前备份的 /opt/centos7.tar.gz 镜像还原。如果非root用户,操作opt目录需要sudo权限
docker load -i /opt/centos7.tar.gz
注:参数 -i
即 input
入的意思。
与前面的结合记忆:
容器就像装了操作系统的虚拟机。
# (1)查看本地正在运行的容器信息
docker ps
#(2)查看本地的所有容器信息
docker ps -a
目前没有容器,结果为空:
例:创建一个名为mycontainer的容器,系统为本地存在的(之前pull过的)镜像centos7。创建后启动、再进入该容器(虚拟机)
# 创建一个名为mycontainer的容器,系统为本地存在的(之前pull过的)镜像centos7
docker create -it --name mycontainer docker.io/centos:7 /bin/bash
# 或 docker create -it --name mycontainer centos:7 /bin/bash
# 启动容器。容器名为mycontainer
docker start mycontainer
# 进入该容器
docker exec -it mycontainer /bin/bash
讲解:
docker create -it --name mycontainer docker.io/centos:7 /bin/bash
-i
: input。表示将标准输入设置为始终打开状态。-t
: terminal。表示附加一个伪终端。-i
和-t
可以合并在一起写成-it
。--name mycontainer
: 表示添加一个名为mycontainer(自定义)的容器。docker.io/centos:7
: 表示在 本地找寻pull好的 centos7镜像(前面 docker pull centos:7)。 若写成 centos,则表示默认寻找lastest版本(最新版)的镜像,若之前没有拉取最新版镜像(docker pull centos),则会报错找不到。docker.io表示Ubuntu发行版。也可省略docker.io/
。其中,docker-ce 是 docker 官方维护的,docker.io 是 Debian 团队维护的。bin/bash
: 表示之后启动进入容器后自动运行bash(打开终端)。docker start mycontainer
# 启动容器。容器id为9842e2c8900f
docker start 9842e2c8900f
但我更喜欢以容器名启动,因为输入ID太麻烦。(启动容器这句话有时不支持输入Tab自动生成)
docker exec -it mycontainer /bin/bash
docker exec -it 9842e2c8900f /bin/bash
但因为输入ID太麻烦,还是选择以容器名启动更方便(而且,进入容器这句话支持输入Tab自动生成)
进入容器后退出容器要用 exit
命令(ctrl + c 不管用的),如下:
另外,再说一下各个参数:
/bin/bash
: 同样表示启动进入容器后自动运行bash(打开终端)。如果不加就会报错,错误示例如下图:
-i
: input。表示将标准输入设置为始终打开状态。如果不加 -i 参数,虽然能进入容器,但输入任何命令都不管用!(由于标准输入没有被打开所以不会被识别),错误示例如下图:
-t
: terminal。表示附加一个伪终端。如果不加这个参数会进不去该容器,一直卡顿,错误示例如下图:
创建容器的同时直接启动并进入该容器,直接将上面创建容器的语句 create
命令改成 run
即可!
docker run -it --name mycontainer docker.io/centos:7 /bin/bash
进入容器后退出容器要用 exit
命令(ctrl + c 不管用的),如下:
# 停止容器。容器名为mycontainer
docker stop mycontainer
# 同理,也可使用容器ID停止。
docker stop 9842e2c8900f
# 重启容器。容器名为mycontainer
docker restart mycontainer
# 同理,也可使用容器ID重启。
docker restart 9842e2c8900f
# 删除容器。容器名为mycontainer
docker rm mycontainer
# 同理,也可使用容器ID删除。
docker rm 9842e2c8900f
如果容器正在运行,需先将容器stop后才能删除!或者也可使用 -f 参数强制删除!
# 强制删除正在运行的容器。容器名为mycontainer
docker rm -f mycontainer
# 同理,也可使用容器ID强制删除。
docker rm -f 9842e2c8900f
我们创建三个容器:
# 创建3个容器,容器名为mycontainer, test, test2
docker create -it --name mycontainer docker.io/centos:7 /bin/bash
docker create -it --name test docker.io/centos:7 /bin/bash
docker create -it --name test2 docker.io/centos:7 /bin/bash
创建后使用docker ps -a
查看一下:
要想批量操作所有容器,我们首先要获取到所有容器的ID或者name。前面讲过查看本地的所有容器信息的命令是
docker ps -a
如果只显示容器ID,可以再加个-q
参数,即:
docker ps -a -q
结果如下:
所以,接下来所有批量操作命令,都可以借助于将docker ps -a -q
当成变量,写成:$(docker ps -a -q)
。具体如下:
# 批量启动所有容器
docker start $(docker ps -a -q)
已批量开启所有容器,即容器ID为c8a3f3e88301, 76077156505d, 9842e2c8900f
的三个容器,如下:
# 批量停止所有容器
docker stop $(docker ps -a -q)
已批量停止所有容器,即容器ID为c8a3f3e88301, 76077156505d, 9842e2c8900f
的三个容器,如下:
# 批量重启所有容器
docker restart $(docker ps -a -q)
已批量重启所有容器,即容器ID为c8a3f3e88301, 76077156505d, 9842e2c8900f
的三个容器,如下:
正在运行的容器要先stop才能删除,或者用-f
参数强制删除。
# 强制删除所有容器(包括正在运行的)
docker rm -f $(docker ps -a -q)
已批量强制删除所有容器,即容器ID为c8a3f3e88301, 76077156505d, 9842e2c8900f
的三个容器,如下:
docker inspect 容器名
例:
docker inspect myubuntu20.04Container
信息很长,部分截图如下:
可以在创建容器的时候设置挂载目录。
例:创建容器名为my_volumes1,创建后在该容器内自动生成目录 /volumes_usb
作为挂载卷。
docker run -it --name my_volumes1 -v /volumes_usb ubuntu:20.04 /bin/bash
结果:
可看到执行上面命令后,新容器中已自动生存 /volumes_usb
目录。
例:创建容器名为my_volumes2,并将主机的 /home/hadoop/mydocker/mnt
目录挂载到新容器my_volumes2中自动生成的 /volumes_usb
目录下。
docker run -it --name my_volumes2 -v /home/hadoop/mydocker/mnt/:/volumes_usb ubuntu:20.04 /bin/bash
############ docker语法习惯,永远记住:冒号左边是主机,右边是容器! #############
说明:创建后在该容器内自动生成目录 /volumes_usb
作为挂载卷,并将主机的 /home/hadoop/mydocker/mnt
目录挂载到新容器my_volumes2中的 /volumes_usb
目录下。
注:上面的语句执行后,既会在主机中自动生成/home/hadoop/mydocker/mnt
文件夹(如果没有),也会在新容器my_volumes2中自动生成/volumes_usb
文件夹。即:在 /home/hadoop/mydocker/
目录下也可以不事先新建好 mnt/
文件夹,因为执行上面的命令会自动生成该目录。
结果:
可看到执行命令后,新容器中已自动生存 /volumes_usb
目录,主机中有/home/hadoop/mydocker/mnt
目录。
在生产环境中,有时候容器间需要共享数据,这时就需要先创建一个已经挂载了卷的容器,然后把这个容器作为专门的数据卷容器提供给其他容器挂载。
挂载容器需要 --volumes-from
参数。
例:新建my_volumes3容器,并将前面创建好的my_volumes1容器的卷(/volumes_usb
目录)挂载到my_volumes3容器上。
docker run -it --name my_volumes3 --volumes-from my_volumes1 ubuntu:20.04 /bin/bash
结果:
可以看到新容器my_volumes3已挂载上my_volumes1容器的卷(/volumes_usb
目录)。
测试:
我们再新开一个终端,进入my_volumes1容器,在其挂载卷/volumes_usb
目录下新建一个文件1.txt,如下:
然后再回到新容器my_volumes3中查看其挂载的/volumes_usb
目录,可已看新文件1.txt,如下:
容器之间是相互隔离的,可以使用link
命令,在两个容器之间建立一条信道,使容器内的应用实现通信。验证是否通信用ping
命令即可!
下面以centos为例,因为docker pull的centos自带ping
命令,而ubuntu没有自带需要下载安装。
# 创建一个名为server的容器
docker run -it --name server centos:7 /bin/bash
再开一个终端
# 创建一个名为client的容器,并连接容器server。(注意容器server要保证启动状态,才能连接上)
docker run -it --name client --link server:server centos:7 /bin/bash
注:如果不想再开一个终端,可以exit退出server容器后,再启动server容器!如下:
通信验证:
在client容器中,终端输入
ping server
能ping通即表示容器client成功连通上容器server!全部步骤如下:
注意:
link这种连通方式是单向的,而非双向,即client能连接上server,但server却不能连接上client。试着在server容器去ping
client容器会发现ping不通、找不到。如下图:
如果需要在server容器中连接client容器,就需要在sever容器端作额外的配置,然而,在生产环境中,由于承载不同服务的容器很多,使用link参数构建网络就不可取了,这会导致配置增多,加大运维的难度。怎么办?下面容器网络解决。
容器网络network可实现网络的互通互联。
docker 自带3种网桥,输入
docker network ls
可看到,如下:
自定义网桥后,再将需要互通的容器添加到自定义的网桥中,便可以实现容器间的互通了。
为什么自定义网桥呢?因为自定义网桥比默认的网桥隔离性好,而且方便。因此在设计容器网络尤其网络互通时候应优先选择自定义网桥。
# 创建一个自定义的网桥,新网桥名为customize_bridge
docker network create -d bridge customize_bridge
验证: 此时再次输入 docker network ls
,可以看到多了自定义的网桥customize_bridge,网桥驱动模式为默认的bridge模式, 如下:
前面已经创建过client和server容器。以client容器为例:
# 将已存在的容器client,网络连接使用customize_bridge网桥网络。
docker network connect customize_bridge client
# 新建容器client1,采用centos7镜像,使用customize_bridge网桥网络。
docker run -it --name client1 --network customize_bridge centos:7 /bin/bash
docker start client
docker start client1
docker inspect customize_bridge
可以看到client容器和client1容器都在customize_bridge网桥网络内,如下图:
注:如果容器没启动用这个命令是看不到的。
docker pull ubuntu:20.04
将已有的容器mycontainer打包提交成镜像p1_image:v1。
docker commit mycontainer p1_image:v1
将镜像保存成文件 p1_image_v1.tgz
docker save -o p1_image_v1.tgz p1_image:v1
docker load -i /路径/p1_image_v1.tgz
docker load -i /home/hadoop/mydocker/images/ubuntu20.04.tar.gz
# -i 表 input
docker import /路径/p1_image_v1.tgz p1_image:v1.0
docker import /home/hadoop/mydocker/images/ubuntu20.04.tar.gz ubuntu:v2
与load不同的是,import可以自定义导入后的镜像名和版本,而load载入是还原的意思,不能自定义,即原来备份的ubuntu20.04.tar.gz 中是什么镜像名和版本,经load载入还原后就还是什么镜像名和版本。
Dockerfile创建镜像。后面第3节讲解。
假设本地有镜像ubuntu:20.04。(通过dokcer images查看后)
docker run -it --name mycontainer ubuntu:20.04 /bin/bash
docker create -it mycontainer
docker commit mycontainer ubuntu:20.04
先来干货,直接上个例子!
通过本地下载好的基础镜像 ubuntu:20.04,在该镜像中安装 jdk 和 tomcat 以后将其制作为一个新的镜像 myubuntu:20.04。制作后将镜像(类似系统)放在容器(类似虚拟机)中,容器名为 myubuntu20.04Container。
假设电脑用户名为hadoop
。docker中存在镜像 ubuntu:20.04,如下图:
先在主目录即/home/hadoop
下新建文件夹 bak
,在 bak
文件夹下存放网上下载好的软件 jdk-8u271-linux-x64.tar.gz
和 apache-tomcat-9.0.59.tar.gz
,其中:
apache-tomcat-9.0.59.tar.gz
解压后文件夹名为:apache-tomcat-9.0.59
,jdk-8u271-linux-x64.tar.gz
解压后文件夹名为:jdk1.8.0_271
。如下图:
再在主目录即/home/hadoop
下新建文件夹 mydocker
,在 mydocker
文件夹下新建文件Dockerfile,文件Dockerfile内容如下:
# 基础镜像 FROM ubuntu:20.04 # 维护者信息 LABEL maintainer="111@qq.com" # 下面这种写法官方不建议了 # MAINTAINER my first image 111@qq.com # 设置工作目录 WORKDIR /mnt # 新镜像构建成功以后创建指定目录 RUN mkdir -p /usr/local/java && mkdir -p /opt/tomcat # 拷贝文件到镜像中并解压 ADD jdk-8u271-linux-x64.tar.gz /usr/local/java ADD apache-tomcat-9.0.59.tar.gz /opt/tomcat # 暴露容器运行时的 8080 监听端口给外部 EXPOSE 8080 # 设置容器内 JAVA_HOME 环境变量 ENV JAVA_HOME /usr/local/java/jdk1.8.0_271/ ENV PATH $PATH:$JAVA_HOME/bin # 启动容器时启动tomcat并查看tomcat日志信息 ENTRYPOINT /opt/tomcat/apache-tomcat-9.0.59/bin/startup.sh && tail -f /opt/tomcat/apache-tomcat-9.0.59/logs/catalina.out
输入
docker build -f Dockerfile -t myubuntu:20.04 /home/hadoop/bak
成功结果如下图:
此时输入 docker images
可以看到新建的镜像 myubuntu:20.04
,如下图:
可以用下面一句话完成:
docker run -it --name myubuntu20.04Container -p 8080:8080 myubuntu:20.04
结果如下图,可看到启动容器时tomcat已自动启动(正如Dockerfile设置的那样):
此时输入 docker ps
可看到正在运行的容器myubuntu20.04Container,其镜像用的是 myubuntu:20.04,如下:
a)以命令终端打开进入装了新镜像的新容器,输入
docker exec -it myubuntu20.04Container /bin/bash
可看到进入后默认的工作目录是/mnt(在Dockerfile定义),jdk已安装在/usr/local
下,tomcat已安装在/opt
下,如下图:
b)在主机输入ifconfig
,可以看到有一个名为docker0
的网卡,ip地址为172.17.0.1
,即是新容器镜像的地址(类似新虚拟机地址)。如下:
在主机中访问这个地址的8080端口,浏览器输入172.17.0.1:8080
即可访问我们刚刚新建的新镜像容器的tomcat了,如下图:
附:(这个了解即可)也可不用docker网卡地址,也可用主机网卡地址,ifconfig可以看到(不是localhost)。
下面结合Dockerfile常用指令知识,对上面的步骤进行讲解。
FROM <image>:<tag>
# 基础镜像
FROM ubuntu:20.04
MAINTAINER <name>
# 维护者信息(不推荐,已过时)
MAINTAINER Ace 111@qq.com
LABEL <key>=<value> <key>=<value> <key>=<value> ...
# 维护者信息(不推荐,已过时)
LABEL maintainer="111@qq.com"
RUN <command>
# 新镜像构建成功以后创建指定目录
RUN mkdir -p /usr/local/java && mkdir -p /opt/tomcat
ADD <src>... <dest>
# 拷贝文件到镜像中并解压
ADD jdk-8u271-linux-x64.tar.gz /usr/local/java
COPY <src>... <dest>
# 拷贝文件到镜像中(不解压)
COPY jdk-8u271-linux-x64.tar.gz /usr/local/java
EXPOSE <port> [<port>/<protocol>...]
# 暴露容器运行时的 8080 监听端口给外部
EXPOSE 8080
如果想暴露多个端口的话:
# 暴露容器运行时的 80,443,和 8080 监听端口给外部
EXPOSE 80 443 8080/tcp
# 设置容器内 JAVA_HOME 环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_271/
ENV PATH $PATH:$JAVA_HOME/bin
CMD ["executable","param1","param2"]
,如:CMD ["/usr/local/tomcat/bin/catalina.sh", "start"]
CMD ["param1","param2"]
,如:CMD [ "echo", "$JAVA_HOME" ]
CMD command param1 param2
,如:CMD echo $JAVA_HOME
docker run -it --name myubuntu20.04Container myubuntu:20.04 echo "helloworld"
或者 docker run -it --name myubuntu20.04Container myubuntu:20.04 /bin/bash
,就不会输出 $JAVA_HOME 的环境变量信息了,因为 CMD 命令被 echo "helloworld"
、/bin/bash
命令覆盖了。# 启动容器时输出 $JAVA_HOME 信息(但是会被docker run启动命令行指定参数所覆盖)
CMD ehco $JAVA_HOME
ENTRYPOINT
语法:
ENTRYPOINT ["executable", "param1", "param2"]
,如:ENTRYPOINT ["/usr/local/tomcat/bin/catalina.sh", "start"]
ENTRYPOINT command param1 param2
,如:ENTRYPOINT ehco $JAVA_HOME
功能:启动容器时执行的 Shell 命令,同 CMD 类似,不会被 docker run 命令行指定的参数所覆盖。在 Dockerfile 中只能有一条 ENTRYPOINT 指令。如果设置了多条 ENTRYPOINT,只有最后一条 ENTRYPOINT 会生效。
例如:
# 启动容器时输出 $JAVA_HOME 信息(不会被docker run启动命令行指定参数所覆盖)
ENTRYPOINT ehco $JAVA_HOME
# 启动容器时启动tomcat并查看tomcat日志信息
ENTRYPOINT /opt/tomcat/apache-tomcat-9.0.59/bin/startup.sh && tail -f /opt/tomcat/apache-tomcat-9.0.59/logs/catalina.out
注意:
如果在 Dockerfile 中同时写了 ENTRYPOINT 和 CMD,
并且 CMD 指令不是一个完整的可执行命令,
那么 CMD 指定的内容将会作为 ENTRYPOINT 的参数;
如果在 Dockerfile 中同时写了 ENTRYPOINT 和 CMD,
并且 CMD 是一个完整的指令,那么它们两个会互相覆盖,谁在最后谁生效
WORKDIR 目录名
WORKDIR /mnt
打开终端后自动在/mnt目录下
若工作目录放在/usr/local
下,再用RUN、CMD、 COPY 和 ADD 等命令就默认在/usr/local下使用了,方便的多了:
WORKDIR /usr/local
VOLUME ["目录名"]
# 容器的 /var/lib/mysql 目录会在运行时自动挂载为匿名卷,匿名卷在宿主机的 /var/lib/docker/volumes 目录下
VOLUME ["/var/lib/mysql"]
docker build -f Dockerfile -t myubuntu:20.04 /home/hadoop/bak
docker build
构建-f Dockerfile
中的 -f
是 file的意思,-f
后面接Dockfile的路径,来识别 Dockerfile。当前目录即 ./Dockerfile-t
是terminal的意思,表示附加一个伪终端。常和-i
(表标准输入设置为始终打开状态)一起写成-it
myubuntu:20.04
是使用Dockerfile创建镜像后的新镜像名。/home/hadoop/bak
表构建新镜像时在本机该目录下找软件(像下载好的jdk,tomcat软件事先放在这里),构建镜像时该路径下所有文件都打包上传给Docker引擎。这个有点类似我们平时我们用的主机和虚拟机之间的共享目录。/home/hadoop/mydocker
下了,如下:.
来代替,表当前路径,类似于linux的 ./
。利用Dockerfile构建新镜像的命令可以间写成:# 注意:末尾有个点 "."
docker build -f Dockerfile -t myubuntu:20.04 .
另外,因为Dockerfile是当前目录的, Dockerfile也可以省略,会自动搜索当前目录下的Dockerfile文件。
# 注意:末尾有个点 "."
docer build -t myubuntu:20.04 .
docker run -it --name myubuntu20.04Container -p 8080:8080 myubuntu:20.04
docker run
:创建容器后立即运行并进入容器。-it
:-i
即input,表将标准输入设置为始终打开状态。-t
即terminal,表附加一个伪终端,-i
和-t
可以合并在一起写。--name myubuntu20.04Container
:新容器名-p 8080:8080
:暴露的端口。和主机交互。创建时即要添加容器和主机的端口映射。myubuntu:20.04
: 前面创建的新镜像名假设电脑用户名为hadoop
。
(1)在主目录即/home/hadoop
下新建文件夹 mydocker
,在 mydocker
文件夹下新建文件Dockerfile
,
(2)在网上下载好软件 jdk-8u271-linux-x64.tar.gz
和 apache-tomcat-9.0.59.tar.gz
,并放在mydocker
文件夹下,其中:
apache-tomcat-9.0.59.tar.gz
解压后文件夹名为:apache-tomcat-9.0.59
,jdk-8u271-linux-x64.tar.gz
解压后文件夹名为:jdk1.8.0_271
。(3)Dockerfile
文件内容:
# 基础镜像 FROM ubuntu:20.04 # 维护者信息 LABEL maintainer="111@qq.com" # 下面这种写法官方不建议了 # MAINTAINER my first image 111@qq.com # 设置工作目录 WORKDIR /usr/local # 新镜像构建成功以后创建指定目录 RUN mkdir -p /usr/local/java # 拷贝文件添加到镜像中并解压 ADD jdk-8u271-linux-x64.tar.gz /usr/local/java ADD apache-tomcat-9.0.59.tar.gz /opt/ # 重命名文件夹 RUN mv /opt/apache-tomcat-9.0.59 /opt/tomcat # 暴露容器运行时的 8080 监听端口给外部 EXPOSE 8080 # 设置容器内 JAVA_HOME 环境变量 ENV JAVA_HOME /usr/local/java/jdk1.8.0_271/ ENV PATH $PATH:$JAVA_HOME/bin # 启动容器时启动tomcat并查看tomcat日志信息 ENTRYPOINT /opt/tomcat/bin/startup.sh && tail -f /opt/tomcat/logs/catalina.out
终端执行
# 创建镜像myubuntu:20.04。注意!末尾有个点 “.”
# docker build -f Dockerfile -t myubuntu:20.04 .
# 上面的Dockerfile也可省略(默认自动搜索当前目录下的Dockerfile文件),如下:
docker build -t myubuntu:20.04 .
# 创建并启动进入容器myubuntu20.04Container。
docker run -it --name myubuntu20.04Container -p 8080:8080 myubuntu:20.04
另开一个终端
# 查看所有的镜像。可以看到新镜像myubuntu:20.04
docker images
# 查看所有的容器。可以看到新容器myubuntu20.04Container
docker ps -a
# 查看正在运行的容器。可以看到新容器myubuntu20.04Container已经开启
docker ps
# 关闭容器
docker stop myubuntu20.04Container
# 再次启动容器
docker start myubuntu20.04Container
# 以命令行终端模式进入容器。进入后可以用 exit 命令退出。
docker exec -it myubuntu20.04Container /bin/bash
附:
因为Dockerfile设置了工作目录,所以Dockerfile如创建文件夹等之类的操作可以不写绝对路径,使用相对路径。Dockerfile再次精简后的写法如下:
# 基础镜像 FROM ubuntu:20.04 # 维护者信息 LABEL maintainer="111@qq.com" # 下面这种写法官方不建议了 # MAINTAINER my first image 111@qq.com # 设置工作目录 WORKDIR /usr/local # 新镜像构建成功以后创建指定目录 RUN mkdir java # 拷贝文件到镜像中并解压 ADD jdk-8u271-linux-x64.tar.gz ./java ADD apache-tomcat-9.0.59.tar.gz /opt/ RUN mv /opt/apache-tomcat-9.0.59 /opt/tomcat # 暴露容器运行时的 8080 监听端口给外部 EXPOSE 8080 # 设置容器内 JAVA_HOME 环境变量 ENV JAVA_HOME /usr/local/java/jdk1.8.0_271/ ENV PATH $PATH:$JAVA_HOME/bin # 启动容器时启动tomcat并查看tomcat日志信息 ENTRYPOINT /opt/tomcat/bin/startup.sh && tail -f /opt/tomcat/logs/catalina.out
我们用docker pull下的镜像都是精简版的,装在容器中,一些基本的命令如vim
都没有。而用 apt install vim
又提示找不到。这个问题可以通过给容器内镜像系统配置国内镜像源来解决。
进入docker 装了ubuntu20.04系统的容器后,改/etc/apt/sources.list
文件,但由于没有vim
和gedit
命令,所以我们可以有两种方式:
echo
命令来修改文件内容。# 1. 先备份
cd /etc/apt
sudo cp sources.list sources.list.bak
# 2. 向/etc/apt/sources.list添加网易源
echo 'deb http://mirrors.163.com/debian/ jessie main non-free contrib' >> /etc/apt/sources.list && \
echo 'deb http://mirrors.163.com/debian/ jessie-updates main non-free contrib' >> /etc/apt/sources.list && \
echo 'deb http://mirrors.163.com/debian-security/ jessie/updates main non-free contrib' >> /etc/apt/sources.list
# 3. 更新源,这步骤不可少
apt-get update
接下来便可使用apt install vim
来按装 vim 了。
说明:
(1)docker容器,默认root用户,是没有sudo命令的。
(2)这里推荐使用网易的源。阿里/中科大/清华的源有几率报错。比如在apt-get update的时候,阿里的源可能报错(Ubuntu的提示:the public key is not available)。可能是Docker提供的精简Ubuntu,在阿里的源下载验证无法通过。
Docker pull的镜像默认来自官方的镜像仓库Docker Hub,一般我们会配置国内镜像仓库如阿里云、网易云等,这些仓库都是公用的,或者托管在第三方平台上的。但商业软件出于保密的需求,需要建立一个私有仓库。
用户可以使用Dokcer官方提供的镜像registry来搭建私有仓库。
# 拉取registry镜像,不指定版本默认为最新的latest版本。
docker pull registry
输入 docker images
可以看到已经被pull下来了:
docker run -d -p 5000:5000 --name=local_registry --restart=always --privileged=true -v /home/hadoop/mydocker/images:/var/lib/registry registry:latest
-d
: 容器以后台服务的形式运行-p 5000:5000
: registry镜像默认监听了5000端口,因此利用run
命令在创建、启动容器时候要使用-p
指定端口映射。--restart=always
:容器在退出时总是重启。--privileged=true
:获取容器的特权,类似ubuntu系统的普通账户获取了root权限。-v /home/hadoop/mydocker/images:/var/lib/registry registry:latest
:将镜像上传到这个新建的local_registry容器的/var/lib/registry
目录下,通过 -v
在宿主机和容器之间建立映射,就可以将容器保存到宿主机的 -v /home/hadoop/mydocker/images
目录下。registry:latest
: 为前面刚拉取的 registry镜像,为最新版本 latest。(类似于ubuntu:20.04, centos:7)发布镜像时,需要将image重新命名,即用tag标签起别名。否则会报错denied: requested access to the resource is denied
。
注意: 这里起别名不能乱起,开头要有标识,以便上传到仓库能被识别。
这里我们的命名格式为: localhost:5000/镜像名:版本。
例: 假设本地存在镜像ubuntu:20.04,要将其发布到上面新建的本地仓库local_registry中。如下:
# 打标签
docker tag ubuntu:20.04 localhost:5000/myubuntu:v1_20220101
# 查看镜像已有重命名的
docker images
# 发布镜像到本地仓库local_registry(localhost:5000)
docker push localhost:5000/myubuntu:v1_20220101
成功发布(push)到私有仓库的运行结果如下图:
Pushed
: 表成功发布。
sha256:9c152418e380c6e6dd7e19567bb6762b67e22b1d0612e4f5074bda6e6040c64a
: 是镜像在仓库的sha256值,sha256是一种加密算法,类似于md5。
上传完毕后,可以打开浏览器,访问 宿主机ip::5000/v2/_catalog
来查看刚上传的镜像。
若在本地,即访问: 127.0.0.1:5000/v2/_catalog
或 localhost:5000/v2/_catalog
。
如下图,可看到上传的自己重命名的myubuntu镜像(前面已tag重命名为localhost:5000/myubuntu:v1_20220101
):
进入到local_registry容器,
注意使用 docker exec -it local_registry /bin/bash
进入失败,因为创建该容器的时候没有指定 /bin/bash。
我们要用sh命令来进入该容器:
docker exec -it local_registry sh
如下:
可以看到push发布的镜像存在local_registry容器中的/var/lib/registry/docker/registry/v2/blobs/sha256
目录下。
sudo apt install docker-compose
通过
which docker-compose
命令,可看到启动快捷位置在
/usr/bin/docker-compose
# 切换root用户
su
pip install docker-compose
验证:
输入
docker-compose -v
可以查看到版本,表示安装成功!如下图:
注意: 一定要切换成root用户安装,否则会因为权限问题导致docker-compose安装在home/用户名/.local/bin 下,导致直接输入docker-compose -v 找不到命令。
附: root用户安装的,切换成普通用户也能直接运行docker-compose,我的切换成名为hadoop
的用户,如下:
那么我们可以直接启动的docker-compose安装在哪里了呢?我用的是anaconda的pip,自然是在anaconda目录下了,可以使用find命令全局搜索docker-compose验证一下,结果如下:
从github下载docker-compose,安装位置为:/usr/local/bin/docker-compose
# 下载安装
curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# 建立软链接
ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
注释:
$(uname -s)
和 $(uname -m)
是为了获取本机的信息,以便下载适合的版本。如下:docker-compose-$(uname -s)-$(uname -m)
即 docker-compose-x86_64_Linux
。cd /usr/local/bin
./docker-compose -v
便可以看到版本信息。
任选一个位置新建一个目录 myflask
(如/home/hadoop/mydocker/myflask
),里面建4个文件:
app.py, docker-compose.yml, Dockerfile, requirements.txt。
app.py:
import time import redis from flask import Flask app = Flask(__name__) cache = redis.Redis(host="redis", port=6379) def get_hit_count(): retries = 5 while True: try: return cache.incr('hits') except redis.exceptions.ConnectionError as exc: if retries == 0: raise exc retries -= 1 time.sleep(0.5) @app.route('/') def hello(): count = get_hit_count() return "Hello World! I have been seen {} times.\n".format(count)
docker-compose.yml:
version: '3'
services:
web:
build: .
ports:
- "5001:5000"
redis:
image: "redis:alpine"
Dockerfile:
FROM ubuntu:20.04
WORKDIR /code
ENV FLASK_APP app.py
ENV FLASK_RUN_HOST 0.0.0.0
RUN apt-get update
RUN apt-get install python3.8 -y
RUN apt-get install python3-pip -y
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
COPY . .
CMD ["flask", "run"]
或
FROM centos:7
WORKDIR /code
ENV FLASK_APP app.py
ENV FLASK_RUN_HOST 0.0.0.0
# 替换阿里镜像加速
# RUN yum -y install epel-release
# RUN yum repolist
RUN yum update -y
RUN yum install python3 -y
COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt
COPY . .
CMD ["flask", "run"]
requirements.txt:
flask
redis
# 进入到刚新建的目录
cd /home/hadoop/mydocker/myflask
# 构建服务
docker-compose up
查看相关容器
docker-compose ps
主机浏览器输入 localhost:5001
可以访问到容器。
使用命令管理容器是非常高效的,但不直观,对新手来说也不容易。所以,还有一个好用的可视化的容器运维工具Portainer。
Portainer提供了一个官方的镜像,简化了部署过程。
# 默认下载最新的latest版本
docker pull portainer/portainer
docker run -d --privileged=true -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v /home/hadoop/mydocker/portainer_data:/portainer_data portainer/portainer:latest
注释:
-p 9000:9000
: 端口,等会主机可通过9000端口访问这个portainer的容器-v /var/run/docker.sock:/var/run/docker.sock
:左主机右容器。 冒号左边是左主机(目录)、 冒号右边是右容器(目录)-v /home/hadoop/mydocker/portainer_data:/portainer_data
: 同理portainer/portainer:latest
: 上面下载的最新latest版本的portianer镜像(可通过docker images查看)。docker ps
查看正在运行的容器,可以看到新容器已创建成功并在运行,新容器名被默认自动设置为了 gracious_goldwasser
,如下:
前面创建容器的时候设定了端口9000,可通过 主机ip:9000
来访问。
本地的话即:localhost:9000
。如下:
首次登陆后让创建新用户,默认admin,密码自定义至少8位,可设置为12345678。进入后,界面如下:
上图,可看到Portianer提供了4种不同的功能选择,由于本机使用的是单机操作Docker,所以选择Local面板,点击Connect
进入主界面。如下:
上图,可以看到所有的容器信息:总共10个容器,4个在运行,6个关闭状态。
接下来,点击上图箭头指向的空白区域可以进入Dashboard仪表盘,这里的信息更为丰富,如下:
点击上图红框的"Containers"面板,可以进入容器列表界面,如下:
在该界面可以管理容器:启动、停住、重启、暂停、删除、添加容器等功能。很直观~
怕占用资源,用systemctl disable docker
将docker自启动关闭后,就启动不了docker了。
Warning: The unit file, source configuration file or drop-ins of docker.service changed on disk. Run 'systemctl daemon-reload' to reloa>
● docker.service - LSB: Create lightweight, portable, self-sufficient containers.
Loaded: loaded (/etc/init.d/docker; generated)
Active: failed (Result: exit-code) since Wed 2022-12-31 19:35:41 CST; 20min ago
Docs: man:systemd-sysv-generator(8)
12月 08 19:35:41 host systemd[1]: Starting LSB: Create lightweight, portable, self-sufficient containers....
12月 08 19:35:41 host docker[10404]: * /usr/bin/dockerd not present or not executable
12月 08 19:35:41 host systemd[1]: docker.service: Control process exited, code=exited, status=1/FAILURE
12月 08 19:35:41 host systemd[1]: docker.service: Failed with result 'exit-code'.
12月 08 19:35:41 host systemd[1]: Failed to start LSB: Create lightweight, portable, self-sufficient containers..
上面 loaded (/etc/init.d/docker; generated)
加载时,但 /usr/bin/dockerd not present or not executable
没有执行。
/etc/init.d/docker
里面确实用到了 dockerd
;/usr/bin/
下只有了 docker
,而没有了 dockerd
,缺失!docker
或 dockerd
都会有提示,而此时:apt install docker.io
会下载dockerd,然后重启dokcer便好用了。
先拉取 mysql8.0 镜像
# 拉取mysql8.0镜像
docker pull mysql:8.0
# 查看镜像
docker images
创建容器,装入拉取好的mysql8.0镜像
# 创建容器并后台运行。主机通过3307端口对应着mysql容器的3306端口
docker run -td --name mysql_db --restart=always -p 3307:3306 -e MYSQL_ROOT_PASSWORD=root mysql:8.0
参数说明:
或者先创建容器,再启动容器,也一样。
docker create -it --name mysql_db --restart=always -p 3307:3306 -e MYSQL_ROOT_PASSWORD=root mysql:8.0
docker start mysql_db
进入容器
docker exec -it mysql_db /bin/bash
# 等价: docker exec -it mysql_db bash
输入 mysql -uroot -p
,密码 root
,能成功连接mysql。
查容器IP。新装的极简版容器一般没有ifconfig
命令,但由于容器和虚拟机一样走的是宿主机的网卡,所以可通过宿主机输入 ifconfig
来间接地查容器ip。
在宿主机输入ifconfig
后,可以看到有一个 docker0
的网卡,我这里显示的ip为 172.17.0.1
。在Navicat上用这个ip去连mysql(注意端口是3307,密码root)。
(注意:如果这个ip连接不上,那么最下面那块网卡有个192.168.开头的ip,试试那个)
如果docker里的mysql存了很多数据了,且要用docker迁移到其他机器中。
i)当前电脑:
# 将容器提交成镜像
docker commit mysql_db mysql_db:v1
# 保存镜像
docker save -o mysql_db_v1.tgz mysql_db:v1
拿到 mysql_db_v1.tgz 文件,拷到另一台电脑。
ii)另一台电脑:
# 加载镜像
docker load -i mysql_db_v1.tgz
# 创建容器。只需要写明与主机对应的端口号即可!其他密码等都和原来镜像里一样。
docker create -it --name mysql_db -p 3307:3306 mysql_db:v1
# 启动容器
docker start mysql_db
同样,可以用navicat等连接测试。
docker pull --platform=架构名 镜像名:版本号
例: 在x86的docker中拉取arm版的mysql:8镜像。
docker pull --platform=arm64 mysql:8
# 或 docker pull --platform=arm64 mysql:8.0
我的docker是20.10版本的,如果上面失败,那么按照下面修改配置文件试试,如下:
# 编辑 /etc/docker/daemon.json,使内容如下: { "registry-mirrors":[ "https://zlwmp571.mirror.aliyuncs.com", "https://docker.mirrors.ustc.edu.cn/", "https://reg-mirror.qiniu.com/", "https://hub-mirror.c.163.com/" ], "experimental":true } # 重新加载服务的配置文件 systemctl daemon-reload # 重启docker systemctl restart docker # 拉取arm版的mysql:8镜像 docker pull --platform=arm64 mysql:8
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。