Docker知识进阶与容器编排技术

By | 2020年10月30日

1 使用Dockerfile定制redis镜像

Docker基础知识总结参考上篇博客:https://www.cnblogs.com/darope/p/13861840.html。

1.1 环境准备

  • redis安装包
  • redis配置文件
  • 编写Dockerfile文件
 ~/opt/docker-redis/ ls
Dockerfile          redis-4.0.14.tar.gz redis-7000.conf
 ~/opt/docker-redis/

配置文件redis-7000.conf:

port 7000
bind 0.0.0.0

1.2 编写Dockerfile文件

我们需要在一个干净的centOs上安装必要的环境。由于redis基于c语言开发,我们需要安装c语言编译环境gccgcc-c++net-tools为网络工具包,make为安装程序的必备组件。Dockerfile文件如下

FROM centos
RUN ["yum" , "install" , "-y" ,"gcc","gcc-c++","net-tools","make"]
WORKDIR /usr/local
ADD redis-4.0.14.tar.gz .
WORKDIR /usr/local/redis-4.0.14/src
RUN make && make install
WORKDIR /usr/local/redis-4.0.14
ADD redis-7000.conf .
EXPOSE 7000
CMD ["redis-server","redis-7000.conf"]

解释:

  • FROM centos表示设置基准镜像为一个centOS环境
  • RUN ["yum" , "install" , "-y" ,"gcc","gcc-c++","net-tools","make"]表示为纯净的centOS系统安装运行redis的tar包的必备工具
  • WORKDIR /usr/local切换工作目录为/usr/local,类似于linux的cd命令
  • ADD redis-4.0.14.tar.gz .拷贝并解压和Dockerfile同级目录下的redis的tar包。拷贝的镜像的当前工作目录下
  • WORKDIR /usr/local/redis-4.0.14/src更换工作目录到已经解压的redis文件夹的src下
  • RUN make && make install在镜像中的linux环境对源码包编译和安装
  • WORKDIR /usr/local/redis-4.0.14工作目录切换到redis的解压目录
  • ADD redis-7000.conf .把与Dockerfile文件同级目录下的redis配置文件拷贝到当前镜像工作空间下
  • EXPOSE 7000开放镜像的7000端口
  • CMD ["redis-server","redis-7000.conf"]启动redis服务

1.3 通过Dockerfile构建镜像

Last login: Tue Oct 27 00:47:28 on ttys001
 ~/ cd opt
 ~/opt/ cd docker-redis
 ~/opt/docker-redis/ ls
Dockerfile          redis-4.0.14.tar.gz redis-7000.conf
 ~/opt/docker-redis/ docker build --tag myredis:1.0 .
Sending build context to Docker daemon  1.745MB
Step 1/10 : FROM centos
 ---> 0d120b6ccaa8
Step 2/10 : RUN ["yum" , "install" , "-y" ,"gcc","gcc-c++","net-tools","make"]
 ---> Running in c0234e893495
CentOS-8 - AppStream                            2.2 MB/s | 5.8 MB     00:02
CentOS-8 - Base                                 1.5 MB/s | 2.2 MB     00:01
CentOS-8 - Extras                                14 kB/s | 8.1 kB     00:00
Dependencies resolved.
================================================================================
 Package               Arch      Version                     Repository    Size
================================================================================
Installing:
 gcc                   x86_64    8.3.1-5.el8.0.2             AppStream     23 M
 
 ...略
 ...略
 ...略

Installed:
  cpp-8.3.1-5.el8.0.2.x86_64
  gcc-8.3.1-5.el8.0.2.x86_64
  gcc-c++-8.3.1-5.el8.0.2.x86_64
  glibc-devel-2.28-101.el8.x86_64
  glibc-headers-2.28-101.el8.x86_64
  isl-0.16.1-6.el8.x86_64
  kernel-headers-4.18.0-193.19.1.el8_2.x86_64
  libgomp-8.3.1-5.el8.0.2.x86_64
  libmpc-1.0.2-9.el8.x86_64
  libpkgconf-1.4.2-1.el8.x86_64
  libstdc++-devel-8.3.1-5.el8.0.2.x86_64
  libxcrypt-devel-4.1.1-4.el8.x86_64
  make-1:4.2.1-10.el8.x86_64
  net-tools-2.0-0.51.20160912git.el8.x86_64
  pkgconf-1.4.2-1.el8.x86_64
  pkgconf-m4-1.4.2-1.el8.noarch
  pkgconf-pkg-config-1.4.2-1.el8.x86_64

Complete!
Removing intermediate container c0234e893495
 ---> 826ee526b28e
Step 3/10 : WORKDIR /usr/local
 ---> Running in 0f3cfeb79b31
Removing intermediate container 0f3cfeb79b31
 ---> c987d8ce6f8c
Step 4/10 : ADD redis-4.0.14.tar.gz .
 ---> f5dad2363617
Step 5/10 : WORKDIR /usr/local/redis-4.0.14/src
 ---> Running in 0bc0c20cbfa3
Removing intermediate container 0bc0c20cbfa3
 ---> c520be237ee0
Step 6/10 : RUN make && make install
 ---> Running in 302720e3f711
    CC Makefile.dep

 ...略
 ...略
 ...略


    INSTALL install
    INSTALL install
    INSTALL install
    INSTALL install
    INSTALL install
Removing intermediate container 302720e3f711
 ---> 90d3292283bd
Step 7/10 : WORKDIR /usr/local/redis-4.0.14
 ---> Running in 1d48fe8e8a0f
Removing intermediate container 1d48fe8e8a0f
 ---> 4061f5b591b1
Step 8/10 : ADD redis-7000.conf .
 ---> 74aa59023e05
Step 9/10 : EXPOSE 7000
 ---> Running in cba837f6acc3
Removing intermediate container cba837f6acc3
 ---> 7a5ebf5ea52c
Step 10/10 : CMD ["redis-server","redis-7000.conf"]
 ---> Running in 5f5dedeb0382
Removing intermediate container 5f5dedeb0382
 ---> d5e04541d181
Successfully built d5e04541d181
Successfully tagged myredis:1.0
 ~/opt/docker-redis/ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myredis             1.0                 d5e04541d181        27 seconds ago      497MB
docker_run          latest              ad84f2ed6200        4 days ago          215MB
myweb               1.0                 bf912fc6c119        4 days ago          647MB
tomcat              latest              891fcd9c5b3a        12 days ago         647MB
centos              latest              0d120b6ccaa8        2 months ago        215MB
 ~/opt/docker-redis/

1.4 通过镜像运行容器

 ~/opt/docker-redis/ docker run -p 7000:7000 myredis:1.0
1:C 26 Oct 16:56:13.709 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 26 Oct 16:56:13.709 # Redis version=4.0.14, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 26 Oct 16:56:13.709 # Configuration loaded
1:M 26 Oct 16:56:13.711 * Running mode=standalone, port=7000.
1:M 26 Oct 16:56:13.711 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:M 26 Oct 16:56:13.711 # Server initialized
1:M 26 Oct 16:56:13.711 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
1:M 26 Oct 16:56:13.711 * Ready to accept connections

可以看到Ready to accept connections表示我们的redis服务端已经启动,等待客户端建立连接。启动redis客户端验证通过。

开启新的终端,进入容器,查看工作目录,文件信息:

 ~/ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
1f8190d4d1de        myredis:1.0         "redis-server redis-…"   8 seconds ago       Up 7 seconds        0.0.0.0:7000->7000/tcp   busy_leavitt
26c97c083e42        bf912fc6c119        "catalina.sh run"        4 days ago          Up 4 days           0.0.0.0:8000->8080/tcp   loving_pasteur
 ~/ docker exec -it 1f8190d4d1de sh
sh-4.4# pwd
/usr/local/redis-4.0.14
sh-4.4# ls
00-RELEASENOTES  BUGS  CONTRIBUTING  COPYING  INSTALL  MANIFESTO  Makefile  README.md  deps  redis-7000.conf  redis.conf  runtest  runtest-cluster  runtest-sentinel  sentinel.conf  src  tests  utils
sh-4.4#

1.5 官方镜像替代我们构建镜像

一般dockerhub上都有官方镜像,我们并不需要每次都自己手动构建镜像,对于官方支持的镜像我们可以直接运行docker pull命令安装,例如docker pull redis,效果和我们自己构建没有什么差别

2 容器间单向通信(Link)

2.1 虚拟ip概念

在docker环境下,容器创建后,都会默认分配一个虚拟ip,该ip无法从外界直接访问,但是在docker环境下各个ip直接是互通互联的。下图假设Tomcat分配的虚拟ip为107.1.31.22,Mysql分配的虚拟ip为107.1.31.24。

虽然我们在Docker环境下可以通过虚拟ip互相访问,但是局限性很大,原因是容器是随时可能宕机,重启的,宕机重启后会为容器重新分配ip,这样原来直接通信的ip就消失了。所以容器间通信不建议通过ip进行通信

容器间单项通信示意图

我们可以通过为容器命名,通过名称通信,这样无论该容器重启多少次,ip如何改变,都不会存在通信不可用的情形。

  • 通过docker run -d --name myweb tomcat命令,使用tomcat镜像运行一个名称为myweb的容器。通过docker run -d --name mydatabases mysql命令,使用mysql镜像运行一个名称为mydatabases的容器
  • 通过docker inspect myweb查看myweb容器的详细配置。其中NetworkSettings下的IPAddress的信息即为docker为该容器分配的虚拟ip地址
 ~/ docker run -d --name myweb tomcat
03fc2187d4ef719325f31578ca07438e1cba4257a0abd9d233755bfb8d9812d2
 ~/ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
03fc2187d4ef        tomcat              "catalina.sh run"        6 seconds ago       Up 5 seconds        8080/tcp                 myweb
1f8190d4d1de        myredis:1.0         "redis-server redis-…"   21 hours ago        Up 21 hours         0.0.0.0:7000->7000/tcp   busy_leavitt
26c97c083e42        bf912fc6c119        "catalina.sh run"        5 days ago          Up 5 days           0.0.0.0:8000->8080/tcp   loving_pasteur
 ~/ docker inspect myweb
[
    {
        "Id": "03fc2187d4ef719325f31578ca07438e1cba4257a0abd9d233755bfb8d9812d2",
        "Created": "2020-10-27T13:48:17.8800933Z",
        "Path": "catalina.sh",
        "Args": [
            "run"
        ],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 6558,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2020-10-27T13:48:18.5295188Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:891fcd9c5b3a174d9ef63832ededae9dc5c986bb1bb66fe35391a4b3a6734804",
        "ResolvConfPath": "/var/lib/docker/containers/03fc2187d4ef719325f31578ca07438e1cba4257a0abd9d233755bfb8d9812d2/resolv.conf",
        "HostnamePath": "/var/lib/docker/containers/03fc2187d4ef719325f31578ca07438e1cba4257a0abd9d233755bfb8d9812d2/hostname",
        "HostsPath": "/var/lib/docker/containers/03fc2187d4ef719325f31578ca07438e1cba4257a0abd9d233755bfb8d9812d2/hosts",
        "LogPath": "/var/lib/docker/containers/03fc2187d4ef719325f31578ca07438e1cba4257a0abd9d233755bfb8d9812d2/03fc2187d4ef719325f31578ca07438e1cba4257a0abd9d233755bfb8d9812d2-json.log",
        "Name": "/myweb",
        "RestartCount": 0,
        "Driver": "overlay2",
        "Platform": "linux",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": null,
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "default",
            "PortBindings": {},
            "RestartPolicy": {
                "Name": "no",
                "MaximumRetryCount": 0
            },
            "AutoRemove": false,
            "VolumeDriver": "",
            "VolumesFrom": null,
            "CapAdd": null,
            "CapDrop": null,
            "Capabilities": null,
            "Dns": [],
            "DnsOptions": [],
            "DnsSearch": [],
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "private",
            "Cgroup": "",
            "Links": null,
            "OomScoreAdj": 0,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": false,
            "ReadonlyRootfs": false,
            "SecurityOpt": null,
            "UTSMode": "",
            "UsernsMode": "",
            "ShmSize": 67108864,
            "Runtime": "runc",
            "ConsoleSize": [
                0,
                0
            ],
            "Isolation": "",
            "CpuShares": 0,
            "Memory": 0,
            "NanoCpus": 0,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": [],
            "BlkioDeviceReadBps": null,
            "BlkioDeviceWriteBps": null,
            "BlkioDeviceReadIOps": null,
            "BlkioDeviceWriteIOps": null,
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpuRealtimePeriod": 0,
            "CpuRealtimeRuntime": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": [],
            "DeviceCgroupRules": null,
            "DeviceRequests": null,
            "KernelMemory": 0,
            "KernelMemoryTCP": 0,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": null,
            "OomKillDisable": false,
            "PidsLimit": null,
            "Ulimits": null,
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0,
            "MaskedPaths": [
                "/proc/asound",
                "/proc/acpi",
                "/proc/kcore",
                "/proc/keys",
                "/proc/latency_stats",
                "/proc/timer_list",
                "/proc/timer_stats",
                "/proc/sched_debug",
                "/proc/scsi",
                "/sys/firmware"
            ],
            "ReadonlyPaths": [
                "/proc/bus",
                "/proc/fs",
                "/proc/irq",
                "/proc/sys",
                "/proc/sysrq-trigger"
            ]
        },
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/fdc6d8ca2570d6bade53746f4db2b36619ef168b83c2f8b31f650943afe477bf-init/diff:/var/lib/docker/overlay2/85b7f801b61243c4b267ca2ad66e60e8739ed444bf78fb06027612a932d1c947/diff:/var/lib/docker/overlay2/74cdb3bef6ede3f9abb989bdc02e374064beebd8aeb7bdf7387f0e3e45d8a088/diff:/var/lib/docker/overlay2/c70ad2ded87f5ab75cc976060f2cce78ee56533b70ce7b06f4be7d8cce6726f5/diff:/var/lib/docker/overlay2/c7bed81095213cd41f384f7fb6dd29f03040fd0dfb22e8e50e9d91fc3166189c/diff:/var/lib/docker/overlay2/93f9bc9528c36bfd401c60a343dec9658ffe4443673474b292cf5f8acbb21954/diff:/var/lib/docker/overlay2/a836c96366b7b5f8c917a53034294a0459f7e6c2ee937ffad66b4f760c690dd1/diff:/var/lib/docker/overlay2/47d65c3061fd960bf4d7f117be0f97c70c270b91d2a6e6e39610b48b90e7f0b0/diff:/var/lib/docker/overlay2/a284464641d4cd773b7c132b05ac0944ecc96bb4348a45b7338d84c73f0209cd/diff:/var/lib/docker/overlay2/f808fbd4a46131f4e7c018f05834422bb6a59035e0c3489daf4908e7f0ef3080/diff:/var/lib/docker/overlay2/7b05a0f542a939883c73f74ee5b71db648dc7ff39108b53566fa160f7c176d4a/diff",
                "MergedDir": "/var/lib/docker/overlay2/fdc6d8ca2570d6bade53746f4db2b36619ef168b83c2f8b31f650943afe477bf/merged",
                "UpperDir": "/var/lib/docker/overlay2/fdc6d8ca2570d6bade53746f4db2b36619ef168b83c2f8b31f650943afe477bf/diff",
                "WorkDir": "/var/lib/docker/overlay2/fdc6d8ca2570d6bade53746f4db2b36619ef168b83c2f8b31f650943afe477bf/work"
            },
            "Name": "overlay2"
        },
        "Mounts": [],
        "Config": {
            "Hostname": "03fc2187d4ef",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "ExposedPorts": {
                "8080/tcp": {}
            },
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/tomcat/bin:/usr/local/openjdk-11/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "LANG=C.UTF-8",
                "JAVA_HOME=/usr/local/openjdk-11",
                "JAVA_VERSION=11.0.8",
                "CATALINA_HOME=/usr/local/tomcat",
                "TOMCAT_NATIVE_LIBDIR=/usr/local/tomcat/native-jni-lib",
                "LD_LIBRARY_PATH=/usr/local/tomcat/native-jni-lib",
                "GPG_KEYS=05AB33110949707C93A279E3D3EFE6B686867BA6 07E48665A34DCAFAE522E5E6266191C37C037D42 47309207D818FFD8DCD3F83F1931D684307A10A5 541FBE7D8F78B25E055DDEE13C370389288584E7 61B832AC2F1C5A90F0F9B00A1C506407564C17A3 79F7026C690BAA50B92CD8B66A3AD3F4F22C4FED 9BA44C2621385CB966EBA586F72C284D731FABEE A27677289986DB50844682F8ACB77FC2E86E29AC A9C5DF4D22E99998D9875A5110C01C5A2F6059E7 DCFD35E0BF8CA7344752DE8B6FB21E8933C60243 F3A04C595DB5B6A5F1ECA43E3B7BBB100D811BBE F7DA48BB64BCB84ECBA7EE6935CD23C10D498E23",
                "TOMCAT_MAJOR=9",
                "TOMCAT_VERSION=9.0.39",
                "TOMCAT_SHA512=307ca646bac267e529fb0862278f7133fe80813f0af64a44aed949f4c7a9a98aeb9bd7f08b087645b40c6fefdd3a7fe519e4858a3dbf0a19c38c53704f92b575"
            ],
            "Cmd": [
                "catalina.sh",
                "run"
            ],
            "Image": "tomcat",
            "Volumes": null,
            "WorkingDir": "/usr/local/tomcat",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {}
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "1ba8bc8211a894e4895e85552eaba33124c85b2efd280c2a8662d1f2683bf6b4",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {
                "8080/tcp": null
            },
            "SandboxKey": "/var/run/docker/netns/1ba8bc8211a8",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "1d5195726db2eee73b97ea994d43b1cef17e0918e1c25925de2e9f0b03ba3616",
            "Gateway": "172.17.0.1",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "172.17.0.4",
            "IPPrefixLen": 16,
            "IPv6Gateway": "",
            "MacAddress": "02:42:ac:11:00:04",
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "a31f18ae4b97ed8e84469a6f48aa03de90c59d054f36705c41ef645a6220a68a",
                    "EndpointID": "1d5195726db2eee73b97ea994d43b1cef17e0918e1c25925de2e9f0b03ba3616",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.4",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:04",
                    "DriverOpts": null
                }
            }
        }
    }
]
 ~/

我们记录mydatabases容器的ip地址,进入myweb容器,用ping命令访问mydatabases的ip。发现是没问题的,说明docker之间运用虚拟ip地址可以直接互相访问。

2.2 配置容器间通过名称访问

  • 把之前的myweb容器。运用docker rm myweb移除掉,保留mydatabases容器。通过docker run -d --name myweb --link mydatabases tomcat来运行容器。其中–link是指定该容器需要和名称为databases的容器通信。
  • 我们进入myweb容器docker exec -it myweb sh,运行命令ping mydatabases,即可通信。我们配置mysql连接的时候,把ip换成mydatabases即可,docker会自动维护mydatabases和该容器ip的映射,即使该容器的ip改变也不会影响访问

3 容器间双向通信(bridge)

通过上文,我们配置容器间互相link,也是可以实现双向通信的,但是有点麻烦。docker为我们提供了网桥,用来快速实现容器间双向通信

3.1 Docker中的虚拟网桥

docker网桥组件概览图
docker网桥组件概览图

docker中的网桥也是虚拟出来的,网桥的主要用途是用来实现docker环境和外部的通信。我们在某个容器内部ping外部的ip例如百度。是可以ping通的。实际上是容器发出的数据包,通过虚拟网桥,发送给宿主机的物理网卡,实际上是借助物理网卡与外界通信的,反之物理网卡也会通过虚拟网桥把相应的数据包送回给相应的容器。

3.2 借助网桥进行容器间通信

我们可以借助网桥实现容器间的双向通信。docker虚拟网桥的另一个作用是为容器在网络层面上进行分组,把不同容器都绑定到网桥上,这样容器间就可以天然互联互通。

  • docker run -d --name myweb tomcat运行myweb容器;docker run -d -it --name mydatabases centos sh交互模式挂起一个databases容器(这里用一个linux容器模拟数据库服务器)。由于没配置网桥,此时容器间无法通过名称互相通信
  • docker network ls列出docker网络服务明细,其中bridge的条目就是docker默认的网桥。接着我们新建立一个网桥docker network create -d bridge my-bridge命名为my-bridge
  • 把容器与我们新建立的my-bridge网桥进行绑定: docker network connect my-bridge myweb,同理: docker network connect my-bridge mydatabases。需要让哪些容器互联互通,就把容器绑定到该网桥上,用来标识这些容器同处在my-bridge网桥的分组中。至此容器间可以通过名称互相访问
 ~/ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
a31f18ae4b97        bridge              bridge              local
9e311308c3ce        host                host                local
2c5f89509739        none                null                local
 ~/ docker network create -d bridge my-bridge
5e678ed577b120f0d95e87ce43d44bab8e15e47f4002428168cad61120c54cc7
 ~/ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
a31f18ae4b97        bridge              bridge              local
9e311308c3ce        host                host                local
5e678ed577b1        my-bridge           bridge              local
2c5f89509739        none                null                local
 ~/ docker network connect my-bridge myweb
 ~/ docker network connect my-bridge mydatabases
 ~/ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
92888ef080a9        centos              "sh"                2 minutes ago       Up 2 minutes                            mydatabases
03fc2187d4ef        tomcat              "catalina.sh run"   43 minutes ago      Up 43 minutes       8080/tcp            myweb
 ~/ docker exec -it 03fc2187d4ef sh
# ping mydatabases
PING mydatabases (172.18.0.3) 56(84) bytes of data.
64 bytes from mydatabases.my-bridge (172.18.0.3): icmp_seq=1 ttl=64 time=0.278 ms
64 bytes from mydatabases.my-bridge (172.18.0.3): icmp_seq=2 ttl=64 time=0.196 ms
64 bytes from mydatabases.my-bridge (172.18.0.3): icmp_seq=3 ttl=64 time=0.417 ms
^C
--- mydatabases ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 55ms
rtt min/avg/max/mdev = 0.196/0.297/0.417/0.091 ms
# exit
 ~/

3.3 网桥通信原理

网桥为什么可以实现容器间的互联互通?实际上,每当我们创建一个网桥,docker都会在宿主机上安装一个虚拟网卡,该虚拟网卡也充当了一个网关的作用。与该网桥(虚拟网关)绑定的容器,相当于处在一个局域网,所以可以通信。虚拟网卡毕竟是虚拟的,如果容器想要和外部通信,仍然需要借助外部(宿主机)的物理网卡。虚拟网卡的数据包进行地址转换,转换为物理网卡的数据包发送出去,反之外部和内部容器通信,也需要进行数据包地址转换

网桥通信原理

4 容器间共享数据(Volume)

4.1 数据卷Volume

场景:很多情形下,我们的web服务都是存在多台相同的服务达到负载均衡的作用。这些web服务在webapp下都存在相同的副本。如果我们要更改页面,那么每个服务都要去更新webapp下的页面文件,才能保证所有的web服务仍然相同。如果我们的服务比较多,甚至达到上千个,那么每个服务更新太过繁琐。为了解决这个问题,我们产生了数据共享方案(Volume)

数据共享的原理,是在宿主机上开辟一块空间,该空间会被其他容器同时共享。我们可以把页面文件存放一份到宿主机的这块磁盘空间中,其他容器挂载这个文件即可。以后更新所有的web服务的页面,我们只需要更改宿主机的这份页面文件即可。

4.2 容器间共享数据-挂载

方法一、docker run --name 容器名 -v 宿主机路径:容器内挂载路径 镜像名。例如docker run --name myweb01 -v /usr/webapps:/usr/local/tomcat/webapps tomcat

方法二、通过设置挂载点,我们可以创建一个用于挂载的容器,其他容器设置参照该容器的挂载信息,进行挂载。例如我们创建一个webpage的容器,设置挂载信息,无需运行该容器:docker create --name webpage -v /webapps:/tomcat/webapps tomcat /bin/true,接着我们创建其他容器参照该容器的挂载信息进行挂载:docker run --volumes-from webpage --name myweb02 -d tomcat,我们创建的myweb02的容器的挂载信息,就和webpage的挂载信息相同。如果需要修改挂载点,只需要修改webpage的挂载信息即可。

5 容器编排技术(docker-compose)

5.1 docker-compose介绍

我们部署一套应用,例如nginx+tomcat+mysql,每个服务可以当成一个容器,每个容器都有自己独立的配置文件,每个容器单独部署如果应用很多,非常麻烦。docker-compose解决了这个问题

通过我们的编排,我们可以自动的先安装mysql容器,再安装tomcat容器,再安装nginx容器,被依赖的容器先安装,完全自动化。这种定义容器安装先后顺序的部署脚本称为容器编排。

Docker-compose 是单机多容器部署工具,通过yml文件定义多容器如何部署,WIN/MAC默认提供Docker Compose,Linux需安装。docker-compose能力有限,只能在一台宿主机上对容器编排部署。集群部署由另外的工具k8s来完成

5.2 安装docker-compose

Mac和Win的桌面版docker自带docker-compose。linux的安装参考官网https://docs.docker.com/compose/

1、安装

sudo curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

2、文件夹授权

sudo chmod +x /usr/local/bin/docker-compose #赋予可执行权限

3、验证docker-compose

 ~/ docker-compose -version
docker-compose version 1.27.4, build 40524192
 ~/

5.3 使用docker-compose快速安装开源博客

参考 https://docs.docker.com/compose/wordpress/

1、创建名为docker-compose.yml的文件

2、编辑文件参考官网直接填入

version: '3.3'

services:
   db:
     image: mysql:5.7
     volumes:
       - db_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: somewordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress

   wordpress:
     depends_on:
       - db
     image: wordpress:latest
     ports:
       - "8000:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress
       WORDPRESS_DB_NAME: wordpress
volumes:
    db_data: {}

3、解释执行该docker-compose文件,运行docker-compose up -d,其中up表示解析该脚本,-d表示后台执行

 ~/opt/docker_compose/ ls
docker-compose.yml
 ~/opt/docker_compose/ docker-compose up -d
Creating network "docker_compose_default" with the default driver
Creating volume "docker_compose_db_data" with default driver
Pulling db (mysql:5.7)...
5.7: Pulling from library/mysql
bb79b6b2107f: Pull complete
49e22f6fb9f7: Pull complete
842b1255668c: Pull complete
9f48d1f43000: Pull complete
c693f0615bce: Pull complete
8a621b9dbed2: Pull complete
0807d32aef13: Pull complete
f15d42f48bd9: Pull complete
098ceecc0c8d: Pull complete
b6fead9737bc: Pull complete
351d223d3d76: Pull complete
Digest: sha256:4d2b34e99c14edb99cdd95ddad4d9aa7ea3f2c4405ff0c3509a29dc40bcb10ef
Status: Downloaded newer image for mysql:5.7
Pulling wordpress (wordpress:latest)...
latest: Pulling from library/wordpress
bb79b6b2107f: Already exists
80f7a64e4b25: Pull complete
da391f3e81f0: Pull complete
8199ae3052e1: Pull complete
284fd0f314b2: Pull complete
f38db365cd8a: Pull complete
1416a501db13: Pull complete
1a45b5b978cd: Pull complete
c662caa8d2ec: Pull complete
2db216a7247d: Pull complete
c3a7647076e8: Pull complete
e40fcea67f94: Pull complete
7f3f9920f7b8: Pull complete
815cf81de52a: Pull complete
680504ca4ff0: Pull complete
9ffcc5a051ce: Pull complete
b9db15beb1db: Pull complete
d5b4974eafaa: Pull complete
0265b92c6601: Pull complete
3342ef871b20: Pull complete
Digest: sha256:6bfe0d4bdb581493c2350da80c48fca089d39315d8fa309bdff7984442e13ba9
Status: Downloaded newer image for wordpress:latest
Creating docker_compose_db_1 ... done
Creating docker_compose_wordpress_1 ... done
 ~/opt/docker_compose/ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
0afa29639779        wordpress:latest    "docker-entrypoint.s…"   55 seconds ago      Up 54 seconds       0.0.0.0:8000->80/tcp   docker_compose_wordpress_1
32333851cdf3        mysql:5.7           "docker-entrypoint.s…"   56 seconds ago      Up 54 seconds       3306/tcp, 33060/tcp    docker_compose_db_1
 ~/opt/docker_compose/

可以看到我们自动安装了两个容器,一个是底层数据库,一个值上层博客应用wordpress。并且在应用的容器上自动做了端口映射。此时我们可以访问127.0.0.1:8000可以看到wordpress的安装向导页面。填写用户名和密码等注册信息,接着登录,博客系统的后台就呈现出来了。

6 docker-dompose实战

项目结构:

 ~/opt/ ls
bsbdj          docker         docker-redis   docker_compose docker_run
 ~/opt/ cd bsbdj
 ~/opt/bsbdj/ ls
bsbdj-app          bsbdj-db           docker-compose.yml
 ~/opt/bsbdj/ cd bsbdj-db
 ~/opt/bsbdj/bsbdj-db/ ls
Dockerfile  init-db.sql
 ~/opt/bsbdj/bsbdj-db/ ls ../bsbdj-app
Dockerfile          application-dev.yml application.yml     bsbdj.jar
 ~/opt/bsbdj/bsbdj-db/

其中~/opt/bsbdj/bsbdj-db/init-db.sql是项目需要的初始化sql脚本。~/opt/bsbdj/bsbdj-app/babdj.jar是由springboot开发的web应用。

6.1 创建Dockerfile为DB构建DB容器

Dockerfile处在~/opt/bsbdj/bsbdj-db/目录下,内容为:

FROM mysql:5.7
# 该目录是mysql5.7提供的初始化数据库的目录,mysql5.7官方定义的,可以参考官方文档
WORKDIR /docker-entrypoint-initdb.d   
ADD init-db.sql .

mysql官方镜像提供,构建mysql镜像时,可以传入环境变量用来设置mysql的root用户的密码。例如:docker run --name mysql01 -e MYSQL_ROOT_PASSWORD=abc123 -d mysql:5.7。Mysql官方提供了大量的环境变量的设置,例如MYSQL_ROOT_PASSWORD表示设置密码,MYSQL_DATABASES设置mysql的数据库,MYSQL_USER和MYSQL_PASSWORD设置另外一个用户和密码。文档还指出,放在/docker-entrypoint-initdb.d目录下的sql文件,在运行容器的过程中会被执行。详情参考mysql官方镜像的文档(https://hub.docker.com/_/mysql)

6.2 创建Dockerfile为App构建web服务容器

Dockerfile处在~/opt/bsbdj/bsbdj-app/目录下,内容为:

FROM openjdk:8u222-jre
WORKDIR /usr/local/bsbdj
ADD bsbdj.jar .
ADD application.yml .
ADD application-dev.yml .
EXPOSE 80   # 由于配置文件中端口开放是80,我们镜像也要开放相应的80端口
CMD ["java","-jar","bsbdj.jar"]

6.3 用docker-compose编排容器

  • 在~/optbsbdj/下创建docker-compose.yml并编写:
version: '3.3'
services:
  db:
    build: ./bsbdj-db/
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root
  app:
    build: ./bsbdj-app/
    depends_on:
      - db
    ports:
      - "80:80"
    restart: always

其中

1、version:’3.3’是docker-compose最常见的版本,不同版本的配置项使用略有差别。

2、services是一个父级节点,表示该编排存在的各种服务。

3、services下级,是各个容器的服务信息,我们这里存在db和app两个服务,db和app也会作为容器的名称,并且我们可以通过服务名替换容器间相互通信的ip(网络的主机名),也可达到互相访问的效果

4、build是服务的子节点,表示该服务容器通过哪里的Dockerfile来构建

5、restart表示服务重启的策略,如果容器意外退出,那么docker会重新启动

6、environment指定服务容器的环境变量

7、depends_on表示该容器的以来,我们的app容器依赖于db服务容器,depends_on下跟服务名

8、ports用来设置该服务的端口和宿主机端口的一个映射,格式为宿主机端口:容器暴露的端口

  • 运行编排脚本,在~/optbsbdj/下执行docker-compose up -d
 ~/opt/bsbdj/ ls
bsbdj-app          bsbdj-db           docker-compose.yml
 ~/opt/bsbdj/ docker-compose up -d
Creating network "bsbdj_default" with the default driver
Building db
Step 1/3 : FROM mysql:5.7
 ---> 1b12f2e9257b
Step 2/3 : WORKDIR /docker-entrypoint-initdb.d
 ---> Running in 75a8bc4e79e2
Removing intermediate container 75a8bc4e79e2
 ---> c66ad7c93eff
Step 3/3 : ADD init-db.sql .
 ---> d5f353210a89

Successfully built d5f353210a89
Successfully tagged bsbdj_db:latest
WARNING: Image for service db was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Building app
Step 1/7 : FROM openjdk:8u222-jre
8u222-jre: Pulling from library/openjdk
9a0b0ce99936: Pull complete
db3b6004c61a: Pull complete
f8f075920295: Pull complete
4901756f2337: Pull complete
9cfcf0e1f584: Pull complete
d6307286bdcd: Pull complete
Digest: sha256:3d3df6a0e485f9c38236eaa795fc4d2e8b8d0f9305051c1e4f7fbca71129b06a
Status: Downloaded newer image for openjdk:8u222-jre
 ---> 25073ded58d2
Step 2/7 : WORKDIR /usr/local/bsbdj
 ---> Running in 3f8dfd0f5274
Removing intermediate container 3f8dfd0f5274
 ---> c4be1b7c9c34
Step 3/7 : ADD bsbdj.jar .
 ---> e63c66e4097a
Step 4/7 : ADD application.yml .
 ---> e670ee9a6aef
Step 5/7 : ADD application-dev.yml .
 ---> 164471f9fdc9
Step 6/7 : EXPOSE 80
 ---> Running in efdae300b488
Removing intermediate container efdae300b488
 ---> 91f9d21b9090
Step 7/7 : CMD ["java","-jar","bsbdj.jar"]
 ---> Running in b2cee3ea8900
Removing intermediate container b2cee3ea8900
 ---> 5e4ed28267e8

Successfully built 5e4ed28267e8
Successfully tagged bsbdj_app:latest
WARNING: Image for service app was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating bsbdj_db_1 ... done
Creating bsbdj_app_1 ... done
 ~/opt/bsbdj/ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                 NAMES
eb5a44f51446        bsbdj_app           "java -jar bsbdj.jar"    4 seconds ago       Up 4 seconds        0.0.0.0:80->80/tcp    bsbdj_app_1
bfc741434b91        bsbdj_db            "docker-entrypoint.s…"   5 seconds ago       Up 4 seconds        3306/tcp, 33060/tcp   bsbdj_db_1
 ~/opt/bsbdj/
  • docker-compose logs用于查看对应的日志,在~/opt/babdj/下执行。docker-compose logs app用来查看app服务的日志信息。
  • docker-compose down 用于停止服务,在~/opt/babdj/下执行。先停止再移除服务容器
 ~/opt/bsbdj/ docker-compose down
Stopping bsbdj_app_1 ... done
Stopping bsbdj_db_1  ... done
Removing bsbdj_app_1 ... done
Removing bsbdj_db_1  ... done
Removing network bsbdj_default
 ~/opt/bsbdj/
  • 修改app的配置文件,把之前mysql的数据库连接的地址localhose,换成db服务名。即可解决数据库连接不上的问题。jdbc:mysql://db:3306/bsbdj?useUnicode=true
  • 访问127.0.0.1,即可看到服务运行情况

docker-compose命令可以参考官方文档,由于docker-compose只支持单机部署,一般大型项目上使用较少,上面的基本命令已经大致满足。如果要应对大型集群的部署,我们可以进而学习k8s

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注