在Linux(Ubuntu)中安装docker
- apt-get remove docker docker-engine docker.io containerd runc
- apt update
- apt-get install ca-certificates curl gnupg lsb-release
- curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
- add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
- apt-get install docker-ce docker-ce-cli containerd.io
- 中途出现问题的话,使用 sudo apt-get update 试试
- systemctl start docker
- apt-get -y install apt-transport-https ca-certificates curl software-properties-common
- service docker restart
- docker run hello-world
- docker version
docker常用命令
帮助命令
docker version # 显示Docker版本信息
docker info # 显示Docker系统信息,包括镜像和容器数量
docker --help # 帮助文档
镜像命令
docker images [options] # 列出本地主机上的镜像
# options
-a: 列出本地所有镜像
-q: 只显示镜像id
docker search [image name] [options] # docker search 某个镜像的名称(对应DockerHub仓库中的镜像)
# options
--filter=stars=50 # 列出收藏数不小于指定值的镜像
docker pull [image name]:[tag] # 下载镜像
docker image rm -f [image name or image id]... # 删除一个或多个镜像
docker image rm -f $(docker images -aq) # 删除本地全部镜像
docker image inspect [image id] # 查看镜像元数据
docker history [image id or image name] # 列出镜像的变更历史
docker login -u [username] # 登录dockerhub
docker push chrishi/mycentos:1.0 # 将镜像发布出去
容器命令
docker run [options] image [command]
# options
--name="Name" # 给容器指定一个名字
-d # 后台方式运行容器,并返回容器的id
-i # 以交互模式运行容器,通常和-t一起使用
-t # 给容器重新分配一个终端,通常和-i一起使用
-P # 随机端口映射(大写)
-p # 指定端口映射(小写),一般可以有四种写法
ip:hostPort:containerPort
ip::containerPort
hostPort:containerPort (常用)
containerPort
docker run -it centos /bin/bash # 使用centos用交互模式启动容器, 在容器内执行/bin/bash命令
# 使用exit退出容器
docker ps [options] # 列出所有运行的容器
# options
-a # 列出当前所有正在运行的容器+历史运行过的容器
-l # 显示最近创建的容器
-n=? # 显示最近n个创建的容器
-q # 只显示容器编号
exit # 容器停止退出
ctrl+P+Q # 容器不停止退出
docker start [container id or container name] # 启动容器
docker restart [container id or container name]# 重启容器
docker stop [container id or container name] # 停止容器
docker kill [container id or container name] # 强制停止容器
docker rm [container id] # 删除指定容器
docker rm -f $(docker ps -aq) # 删除所有容器
docker ps -a -q|xargs docker rm # 删除所有容器
docker logs [options] [container id] # 查看容器日志
# options
-t # 显示时间戳
-f # 打印最新的日志(follow log output)
--tail 数字 # 显示多少条
docker top [container id] # 查看容器中运行的进程信息
docker inspect [container id] #查看容器的元数据
docker exec -it [container id] [bash shell] # 进入正在运行的容器(是在容器中打开新的终端,并且可以启动新的进程)
docker attach [container id] # 直接进入容器启动命令的终端,不会启动新的进程
docker cp 容器id:容器内路径 目的主机路径 # 从容器内拷贝文件到主机上
docker commit -m="提交的描述信息" -a="作者" 容器id 要创建的目标镜像名:[标签名] # 从容器创建一个新的镜像
容器数据卷
挂载方式一:容器中直接使用命令来挂载
docker run -it -v 宿主机绝对路径目录:容器内目录 镜像名 /bin/bash
# eg
docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
挂载方式二:通过Docker File 来挂载
# Dockerfile
FROM centos
VOLUME ["/dataVolumeContainer1","/dataVolumeContainer2"]
CMD echo "-------end------"
CMD /bin/bash
docker build -t chrisshi/centos .
匿名和具名挂载
# 匿名挂载
-v 容器内路径
docker run -d -P --name nginx01 -v /etc/nginx nginx
# 匿名挂载的缺点,就是不好维护,通常使用命令 docker volume维护
docker volume ls
# 具名挂载 -v 卷名:/容器内路径
docker run -d -P --name nginx02 -v nginxconfig:/etc/nginx nginx
docker volume inspect nginxconfig # 查看挂载的路径
# 怎么判断挂载的是卷名而不是本机目录名?
不是/开始就是卷名,是/开始就是目录名
Dockerfile
基础知识
- 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
- 指令按照从上到下,顺序执行
- #表示注释
- 每条指令都会创建一个新的镜像层,并对镜像进行提交
构建步骤
- 编写Dockerfile文件
- docker build 构建镜像
- docker run
构建过程
- docker从基础镜像运行一个容器
- 执行一条指令并对容器做出修改
- 执行类似docker commit 的操作提交一个新的镜像层
- docker再基于刚刚提交的镜像运行一个新容器
- 执行Dockerfile中的下一条指令直到所有指令都执行完成
Dockerfile指令
FROM # 基础镜像,当前新镜像是基于哪个镜像的
MAINTAINER # 镜像维护者的姓名混合邮箱地址
RUN # docker build 时需要运行的命令
EXPOSE # 当前容器对外保留出的端口
WORKDIR # 指定在创建容器后,终端默认登录的进来工作目录,一个落脚点
ARG # docker build 时Dockerfile文件中的变量
ENV # docker run 时设置环境变量
COPY # 拷贝文件和目录到镜像中
ADD # 功能与COPY一样,但是多了自动解压文件的功能
VOLUME # 容器数据卷,用于数据保存和持久化工作
CMD # docker run 时要运行的命令,Dockerfile中可以有多个CMD指令,但只有最后一个生效
ENTRYPOINT # docker run 时要运行的命令,和CMD一样
ONBUILD # 当构建一个被继承的Dockerfile时运行命令,父镜像在被子镜像继承后,父镜像的ONBUILD被触发
CMD:Dockerfifile 中可以有多个CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换!
ENTRYPOINT: docker run 之后的参数会被当做参数传递给 ENTRYPOINT,之后形成新的命令组合!
实例
背景:官方默认的centos镜像默认路径是 / ,默认不支持vim,默认不支持ifcofig
目的:使我们自己的镜像具备如下: 登录后的默认路径(/usr/local)、vim编辑器、查看网络配置ifconfig支持
# Dockerfile
FROM centos:7
MAINTAINER chris.shi<[email protected]>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum update
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "--------end--------"
CMD /bin/bash
# 构建
# docker build -t 新镜像名字:TAG .
docker build -t my-centos:1.0 .
# 运行
# docker run -it 新镜像名字:TAG
docker run -it mycentos:1.0
# 列出镜像的变更历史
docker history [image id]
docker 网络讲解
理解docker0
原理
- 每一个安装了docker的linux主机都有一个docker0的虚拟网卡。这个是桥接网卡,使用了veth-pair技术
root@chris-virtual-machine:~# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 00:0c:29:7a:47:5a brd ff:ff:ff:ff:ff:ff
altname enp2s1
inet 192.168.32.128/24 brd 192.168.32.255 scope global dynamic noprefixroute ens33
valid_lft 5529035sec preferred_lft 5529035sec
inet6 fe80::7a87:6135:1c98:61a9/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:8c:f2:d0:2d brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:8cff:fef2:d02d/64 scope link
valid_lft forever preferred_lft forever
4: br-89830bc6a152: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:78:e0:07:c8 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.1/16 brd 192.168.255.255 scope global br-89830bc6a152
valid_lft forever preferred_lft forever
- 每启动一个docker容器,linux主机就会多一个虚拟网卡
# 我们启动了一个tomcat01(172.17.0.2),主机的ip地址多了一个 24: vethf840e91@if23
docker run -d -P --name tomcat01 tomcat
# 然后我们在tomcat01容器中查看容器的ip是 23: eth0@if24
docker exec -it tomcat01 ip addr
# 我们再启动一个tomcat02(172.17.03)观察
docker run -d -P --name tomcat02 tomcat
# 然后发现linux主机上又多了一个网卡 26: vethe152c3e@if25
# 我们看下tomcat02的容器内ip地址是 25: eth0@if26
# veth-pair 就是一对虚拟设备接口,它都是成对出现的。一端连着协议栈,一端彼此相连着。
# 正因为有这个特性,它常常充当着一个桥梁,连接着各种虚拟网络设备!
# “Bridge、OVS 之间的连接”,“Docker 容器之间的连接” 等等,以此构建出非常复杂的虚拟网络 结构,比如 OpenStack Neutron。
- 测试tomcat01和tomcat02容器间是否可以互相ping通
root@0f3b19b75279:/usr/local/tomcat# ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3): 56 data bytes
64 bytes from 172.17.0.3: icmp_seq=0 ttl=64 time=2.966 ms
64 bytes from 172.17.0.3: icmp_seq=1 ttl=64 time=0.164 ms
64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=0.171 ms
# 结论:容器和容器之间是可以互相访问的
- 结论
tomcat1和tomcat2共用一个路由器。是的,他们使用的一个,就是docker0
任何一个容器启动默认都是docker0网络
docker默认会给容器分配一个可用ip
- 小结
- Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。 Docker容器网络就很好的利用了Linux虚拟网络技术,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth pair)
- Docker中的网络接口默认都是虚拟的接口。虚拟接口的优势就是转发效率极高(因为Linux是在内核中进行数据的复制来实现虚拟接口之间的数据转发,无需通过外部的网络设备交换),对于本地系统和容器系统来说,虚拟接口跟一个正常的以太网卡相比并没有区别,只是他的速度快很多。
--Link
- 思考一个场景,我们编写一个微服务,数据库连接地址原来是使用ip的,如果ip变化就不行了,那我们能不能使用服务名访问呢?
jdbc:mysql://mysql:3306,这样的话哪怕mysql重启,我们也不需要修改配置了!docker提供了 --link 的操作!
# 我们使用tomcat02,直接通过容器名ping tomcat01,不使用ip, 发现是ping不通的
# 我们再启动一个tomcat03,但是启动的时候连接tomcat02
docker run -d -P --name tomcat03 --link tomcat02 tomcat
# 这个时候,我们就可以使用tomcat03 ping通tomcat02了
docker exec -it tomcat03 ping tomcat02
# 再来测试,tomcat03是否可以ping通tomcat01 失败
# 再来测试,tomcat02是否可以ping通tomcat03 反向也ping不通
- 思考,这个原理是什么呢?我们进入tomcat03中查看下host配置文件
root@f86e6836e0a1:/usr/local/tomcat# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3 tomcat02 a263c1e773b2 # 发现tomcat2直接被写在这里
172.17.0.4 f86e6836e0a1
# 所以这里其实就是配置了一个 hosts 地址而已! # 原因:--link的时候,直接把需要link的主机的域名和ip直接配置到了hosts文件中了
--link早都过时了,我们不推荐使用!我们可以使用自定义网络的方式
自定义网络
基本概念
root@chris-virtual-machine:~# docker network --help
Usage: docker network COMMAND
Manage networks
Commands:
connect Connect a container to a network
create Create a network
disconnect Disconnect a container from a network
inspect Display detailed information on one or more networks
ls List networks
prune Remove all unused networks
rm Remove one or more networks
所有网络模式
网络模式 | 配置 | 说明 |
bridge模式 | --net=bridge | 默认值,再docker网桥docker0上为容器创建新的网络栈 |
none模式 | --net=none | 不配置网络,用户可以稍后进入容器,自行配置 |
container模式 | --net=container:name/id | 容器和另外一个容器共享Network namespace。kubernetes中的pod就是多个容器共享一个Network namespace |
host模式 | --net=host | 容器和宿主机共享Network namespace |
用户自定义 | --net=自定义网络 | 用户自己使用network相关命令定义网络,创建容器的时候可以指定为自己定义的网络 |
# 1. 删除原来的所有容器
docker rm -f $(docker ps -aq)
# 2. 接下来我们来创建容器,但是我们知道默认创建的容器都是docker0网卡的
docker run -d -P --name tomcat01 --net bridge tomcat
# docker0网络的特点 1.它是默认的 2.域名访问不通 3.--link 域名通了,但是删了又不行
# 3. 我们可以让容器创建的时候使用自定义网络
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
docker network ls
docker network inspect mynet
# 4. 我们来启动两个容器测试,使用自己的 mynet!
docker run -d -P --name tomcat-net-01 --net mynet tomcat
docker run -d -P --name tomcat-net-02 --net mynet tomcat
docker network inspect mynet
# 5. 我们来测试ping容器名和ip试试,都可以ping通
docker exec -it tomcat-net-01 ping 192.168.0.3
docker exec -it tomcat-net-01 ping tomcat-net-02
发现,我们自定义的网络docker都已经帮我们维护好了对应的关系
所以我们平时都可以这样使用网络,不使用--link效果一样,所有东西实时维护好,直接域名 ping 通
网络联通
docker0和自定义网络肯定不通,我们使用自定义网络的好处就是网络隔离
那关键的问题来了,如何让 tomcat-net-01 访问 tomcat1?
# 1. 启动默认的容器,在docker0网络下
docker run -d -P --name tomcat01 tomcat
docker run -d -P --name tomcat02 tomcat
# 2. 我们来测试一下!打通mynet-docker0
docker network connect mynet tomcat01
docker network inspect mynet # 发现我们的tomcat01就进来这里了,tomcat01拥有了双ip
# 3. tomcat01 可以ping通了
docker exec -it tomcat01 ping tomcat-net-01
# 4. tomcat02 依旧ping不通,大家应该就理解了
结论:如果要跨网络操作别人,就需要使用 docker network connect [OPTIONS] NETWORK CONTAINER 连接