1、docker是什么

docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,是相互隔离的。简言之,就是可以在Linux上镜像快速使用的这么一个容器,部署相当快捷,且轻量。

除此之外,docker容器相当于轻量级服务器,内部部署应用程序,占用的空间是很小的。

例如: centos一般情况下虚拟机,是几G左右,而使用docker下载的centos却只有几百兆。主要还是因为docker下载的centos系统,剔除了bootfs kernel内核,少了许多的不必要,不常用命令,以及一些工具,内核交由是由linux支持的。

2、docker可以做什么

1、打包web应用,使用dockerfile构建镜像,使得多主机可以快速部署应用

2、快速集成 集群环境,例如redis集存,而且可以部署很多。如果不适用docker,部署量有限。

3、在服务型环境中部署和调整数据库或其他的后台应用

4、简单化,部署操作

3、安装docker

  1. docker安装条件

    1.1、需要centos7及以上

    1.2、centos-extras库必须启用

  2. 卸载旧的版本

     sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine
    
  3. 安装存储库,设置稳定的仓库

    sudo yum install -y yum-utils
    # 使用阿里云镜像速度更快哦!
    sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    
  4. 安装docker引擎

    # 默认最新版
    sudo yum install docker-ce docker-ce-cli containerd.io
    

    安装特定版

    # docker版本
    sudo yum list docker-ce --showduplicates | sort -r
    # 安装指定版本
    sudo yum install docker-ce-<VERSION_STRING> docker-ce-cli-<VERSION_STRING> containerd.io
    
  5. 启动docker服务

    sudo systemctl start docker
    
  6. 通过运行hello-world 映像验证 Docker Engine 是否已正确安装。

    sudo docker run hello-world
    # 查看docker
    docker version
    
  7. 卸载docker

    1. 卸载 Docker Engine、CLI 和 Containerd 包:

       sudo yum remove docker-ce docker-ce-cli containerd.io
      
    2. 主机上的映像、容器、卷或自定义配置文件不会自动删除。删除所有镜像、容器和卷

      sudo rm -rf /var/lib/docker
      sudo rm -rf /var/lib/containerd
      

5、docker 常用命令

1、查看docker 信息,版本,命令帮助

docker version
docker info
docker 或者  docker docker-command --help

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ugEnMbrv-1624330369454)(C:\Users\Meet\AppData\Roaming\Typora\typora-user-images\image-20210615120537312.png)]

1、镜像命令

1、查看所有本地镜像

docker images

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HvjcuGbf-1624330369456)(C:\Users\Meet\AppData\Roaming\Typora\typora-user-images\image-20210615120823665.png)]

repository: 镜像仓库源

tag: 版本

image id: 镜像id

created: 创建时间

size: 镜像大小

2、搜索镜像 (以mysql为例)

docker search mysql

3、下载/拉取镜像

docker pull mysql   # 不加版本默认最新版
docker pull mysql:8.0.25  # 获取指定mysql指定版本的镜像(版本信息官方查看)

4、删除镜像

docker rmi --force 镜像id   # 通过指定的镜像id 删除本地镜像
docker rmi --force 镜像id 镜像id 镜像id # 删除多个镜像
docker rmi -f $(docker images -aq) # 删除本地所有镜像

2、容器命令

1、docker run 命令参数

Usage:  docker run [OPTIONS] IMAGE [COMMAND] [ARG...]Run a command in a new containerOptions:--add-host list                  Add a custom host-to-IP mapping (host:ip)-a, --attach list                    Attach to STDIN, STDOUT or STDERR--blkio-weight uint16            Block IO (relative weight), between 10 and 1000, or 0 to disable (default 0)--blkio-weight-device list       Block IO weight (relative device weight) (default [])--cap-add list                   Add Linux capabilities--cap-drop list                  Drop Linux capabilities--cgroup-parent string           Optional parent cgroup for the container--cgroupns string                Cgroup namespace to use (host|private)'host':    Run the container in the Docker host's cgroup namespace'private': Run the container in its own private cgroup namespace'':        Use the cgroup namespace as configured by thedefault-cgroupns-mode option on the daemon (default)--cidfile string                 Write the container ID to the file--cpu-period int                 Limit CPU CFS (Completely Fair Scheduler) period--cpu-quota int                  Limit CPU CFS (Completely Fair Scheduler) quota--cpu-rt-period int              Limit CPU real-time period in microseconds--cpu-rt-runtime int             Limit CPU real-time runtime in microseconds-c, --cpu-shares int                 CPU shares (relative weight)--cpus decimal                   Number of CPUs--cpuset-cpus string             CPUs in which to allow execution (0-3, 0,1)--cpuset-mems string             MEMs in which to allow execution (0-3, 0,1)-d, --detach                         Run container in background and print container ID--detach-keys string             Override the key sequence for detaching a container--device list                    Add a host device to the container--device-cgroup-rule list        Add a rule to the cgroup allowed devices list--device-read-bps list           Limit read rate (bytes per second) from a device (default [])--device-read-iops list          Limit read rate (IO per second) from a device (default [])--device-write-bps list          Limit write rate (bytes per second) to a device (default [])--device-write-iops list         Limit write rate (IO per second) to a device (default [])--disable-content-trust          Skip image verification (default true)--dns list                       Set custom DNS servers--dns-option list                Set DNS options--dns-search list                Set custom DNS search domains--domainname string              Container NIS domain name--entrypoint string              Overwrite the default ENTRYPOINT of the image-e, --env list                       Set environment variables--env-file list                  Read in a file of environment variables--expose list                    Expose a port or a range of ports--gpus gpu-request               GPU devices to add to the container ('all' to pass all GPUs)--group-add list                 Add additional groups to join--health-cmd string              Command to run to check health--health-interval duration       Time between running the check (ms|s|m|h) (default 0s)--health-retries int             Consecutive failures needed to report unhealthy--health-start-period duration   Start period for the container to initialize before starting health-retriescountdown (ms|s|m|h) (default 0s)--health-timeout duration        Maximum time to allow one check to run (ms|s|m|h) (default 0s)--help                           Print usage-h, --hostname string                Container host name--init                           Run an init inside the container that forwards signals and reaps processes-i, --interactive                    Keep STDIN open even if not attached--ip string                      IPv4 address (e.g., 172.30.100.104)--ip6 string                     IPv6 address (e.g., 2001:db8::33)--ipc string                     IPC mode to use--isolation string               Container isolation technology--kernel-memory bytes            Kernel memory limit-l, --label list                     Set meta data on a container--label-file list                Read in a line delimited file of labels--link list                      Add link to another container--link-local-ip list             Container IPv4/IPv6 link-local addresses--log-driver string              Logging driver for the container--log-opt list                   Log driver options--mac-address string             Container MAC address (e.g., 92:d0:c6:0a:29:33)-m, --memory bytes                   Memory limit--memory-reservation bytes       Memory soft limit--memory-swap bytes              Swap limit equal to memory plus swap: '-1' to enable unlimited swap--memory-swappiness int          Tune container memory swappiness (0 to 100) (default -1)--mount mount                    Attach a filesystem mount to the container--name string                    Assign a name to the container--network network                Connect a container to a network--network-alias list             Add network-scoped alias for the container--no-healthcheck                 Disable any container-specified HEALTHCHECK--oom-kill-disable               Disable OOM Killer--oom-score-adj int              Tune host's OOM preferences (-1000 to 1000)--pid string                     PID namespace to use--pids-limit int                 Tune container pids limit (set -1 for unlimited)--platform string                Set platform if server is multi-platform capable--privileged                     Give extended privileges to this container-p, --publish list                   Publish a container's port(s) to the host-P, --publish-all                    Publish all exposed ports to random ports--pull string                    Pull image before running ("always"|"missing"|"never") (default "missing")--read-only                      Mount the container's root filesystem as read only--restart string                 Restart policy to apply when a container exits (default "no")--rm                             Automatically remove the container when it exits--runtime string                 Runtime to use for this container--security-opt list              Security Options--shm-size bytes                 Size of /dev/shm--sig-proxy                      Proxy received signals to the process (default true)--stop-signal string             Signal to stop a container (default "SIGTERM")--stop-timeout int               Timeout (in seconds) to stop a container--storage-opt list               Storage driver options for the container--sysctl map                     Sysctl options (default map[])--tmpfs list                     Mount a tmpfs directory-t, --tty                            Allocate a pseudo-TTY--ulimit ulimit                  Ulimit options (default [])-u, --user string                    Username or UID (format: <name|uid>[:<group|gid>])--userns string                  User namespace to use--uts string                     UTS namespace to use-v, --volume list                    Bind mount a volume--volume-driver string           Optional volume driver for the container--volumes-from list              Mount volumes from the specified container(s)-w, --workdir string                 Working directory inside the container

2、docker run 常用参数

docker run -d 后台方式运行-it 交互式运行容器,进入容器-p 指定容器端口-P 随机指定端口# docker容器独立运行 容器内包含文件系统,网络信息等  相当于小型linux
[root@VM-8-9-centos ~]# docker run -it centos /bin/bash
[root@b5dbd67379d3 /]# ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

3、退出容器

# 容器停止并退出
[root@b5dbd67379d3 /]# exit
exit
[root@VM-8-9-centos ~]# ls
install.sh  # 容器不停止退出
ctrl+ p+q

4、docker ps 查看doker容器

Options:-a, --all             Show all containers (default shows just running)-f, --filter filter   Filter output based on conditions provided--format string   Pretty-print containers using a Go template-n, --last int        Show n last created containers (includes all states) (default -1)-l, --latest          Show the latest created container (includes all states)--no-trunc        Don't truncate output-q, --quiet           Only display container IDs-s, --size            Display total file sizes# 查看正在进行的docker容器
[root@VM-8-9-centos ~]# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES# 查看历史全部docker容器
[root@VM-8-9-centos ~]# docker ps -a
CONTAINER ID   IMAGE          COMMAND       CREATED          STATUS                       PORTS     NAMES
18a72bfec6fe   centos         "/bin/bash"   3 minutes ago    Exited (127) 2 minutes ago             pedantic_mayer
b5dbd67379d3   centos         "/bin/bash"   11 minutes ago   Exited (130) 4 minutes ago             funny_mestorf
d29aae94e8bd   centos         "/bin/bash"   12 minutes ago   Exited (0) 12 minutes ago              relaxed_lehmann
25cd9d50a1e5   d1165f221234   "/hello"      5 hours ago      Exited (0) 5 hours ago                 nice_euler
796cc0845746   d1165f221234   "/hello"      23 hours ago     Exited (0) 23 hours ago                focused_bhaskara

5、删除docker容器

docker rm 容器id
docker rm -f 容器id
docker rm --force  $(docker ps -aq)  # 删除全部容器

6、启动停止容器

docker start 容器id
docker run -d image
docker run image    # 启动的容器,立马就关闭了
docker restart 容器iddocker stop 容器id
docker kill 容器id

7、后台启动容器

docker run -d image

8、docker logs 查看容器日志

Usage:  docker logs [OPTIONS] CONTAINERFetch the logs of a containerOptions:--details        Show extra details provided to logs-f, --follow         Follow log output--since string   Show logs since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)-n, --tail string    Number of lines to show from the end of the logs (default "all")-t, --timestamps     Show timestamps--until string   Show logs before a timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)# docker logs options container
[root@VM-8-9-centos ~]# docker logs -ft b5dbd67379d3
[root@b5dbd67379d3 /]# ls
2021-06-15T07:12:51.949590707Z bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
2021-06-15T07:12:59.531112102Z [root@b5dbd67379d3 /]# ^C
2021-06-15T07:14:40.341797010Z [root@b5dbd67379d3 /]# ^C
2021-06-15T07:14:42.357673686Z [root@b5dbd67379d3 /]# exit
2021-06-15T07:14:42.357695647Z exit

9、docker top 查看容器中正在运行的进程信息

# 注意:只有当容器运行时,才能查看容器中的进程信息
[root@VM-8-9-centos ~]# docker ps
CONTAINER ID   IMAGE     COMMAND       CREATED          STATUS         PORTS     NAMES
18a72bfec6fe   centos    "/bin/bash"   52 minutes ago   Up 3 seconds             pedantic_mayer
[root@VM-8-9-centos ~]# docker top 18a72bfec6fe
UID     PID    PPID     C      STIME    TTY     TIME   CMD
root    11824

10、docker inspect 查看容器元数据

[root@VM-8-9-centos ~]# docker inspect 18a72bfec6fe
[{"Id": "18a72bfec6fefac71ba7c2628cc65d057b5f93146fb2065c0ddd23cddc8ba4a1","Created": "2021-06-15T07:15:59.670973784Z","Path": "/bin/bash","Args": [],"State": {"Status": "running","Running": true,"Paused": false,"Restarting": false,"OOMKilled": false,"Dead": false,"Pid": 1182s,"ExitCode": 0,"Error": "","StartedAt": "2021-06-15T08:08:38.522983332Z","FinishedAt": "2021-06-15T07:16:14.830741174Z"},"Image": "sha256:300e315adb2f96afe5f0b2780b87f28ae95231fe3bdd1e16b9ba606307728f55","ResolvConfPath": "/var/lib/docker/containers/18a72bfec6fefac71ba7c2628cc65d057b5f93146fb2065c0ddd23cddc8ba4a1/resolv.conf","HostnamePath": "/var/lib/docker/containers/18a72bfec6fefac71ba7c2628cc65d057b5f93146fb2065c0ddd23cddc8ba4a1/hostname","HostsPath": "/var/lib/docker/containers/18a72bfec6fefac71ba7c2628cc65d057b5f93146fb2065c0ddd23cddc8ba4a1/hosts","LogPath": "/var/lib/docker/containers/18a72bfec6fefac71ba7c2628cc65d057b5f93146fb2065c0ddd23cddc8ba4a1/18a72bfec6fefac71ba7c2628cc65d057b5f93146fb2065c0ddd23cddc8ba4a1-json.log","Name": "/pedantic_mayer","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,"CgroupnsMode": "host","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/e01f7e5d10cf9ea2fd1f71564d3cda5c674890f8cf86ae6426bc51e760a4082b-init/diff:/var/lib/docker/overlay2/0e63e1fc78d9d663dbbfd5aa1de8eff064d7b8bd908a8500a7e0e0f1304cd686/diff","MergedDir": "/var/lib/docker/overlay2/e01f7e5d10cf9ea2fd1f71564d3cda5c674890f8cf86ae6426bc51e760a4082b/merged","UpperDir": "/var/lib/docker/overlay2/e01f7e5d10cf9ea2fd1f71564d3cda5c674890f8cf86ae6426bc51e760a4082b/diff","WorkDir": "/var/lib/docker/overlay2/e01f7e5d10cf9ea2fd1f71564d3cda5c674890f8cf86ae6426bc51e760a4082b/work"},"Name": "overlay2"},"Mounts": [],"Config": {"Hostname": "18a72bfec6fe","Domainname": "","User": "","AttachStdin": true,"AttachStdout": true,"AttachStderr": true,"Tty": true,"OpenStdin": true,"StdinOnce": true,"Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd": ["/bin/bash"],"Image": "centos","Volumes": null,"WorkingDir": "","Entrypoint": null,"OnBuild": null,"Labels": {"org.label-schema.build-date": "20201204","org.label-schema.license": "GPLv2","org.label-schema.name": "CentOS Base Image","org.label-schema.schema-version": "1.0","org.label-schema.vendor": "CentOS"}},"NetworkSettings": {"Bridge": "","SandboxID": "47651674c9ae4e9fcea40287eb008a38a2cb52ef99db96cc4052dd51df89af6f","HairpinMode": false,"LinkLocalIPv6Address": "","LinkLocalIPv6PrefixLen": 0,"Ports": {},"SandboxKey": "/var/run/docker/netns/47651674c9ae","SecondaryIPAddresses": null,"SecondaryIPv6Addresses": null,"EndpointID": "7909b049052f8df832b902f66618e9d7402e649b2fc96e1848be50085bdc99ff","Gateway": "172.17.0.1","GlobalIPv6Address": "","GlobalIPv6PrefixLen": 0,"IPAddress": "172.17.0.2","IPPrefixLen": 16,"IPv6Gateway": "","MacAddress": "02:42:ac:11:00:02","Networks": {"bridge": {"IPAMConfig": null,"Links": null,"Aliases": null,"NetworkID": "53d8b47f55e1eec574530a4bd0bd4116ee7b94e3bc42572faaaf4257c290804e","EndpointID": "7909b049052f8df832b902f66618e9d7402e649b2fc96e1848be50085bdc99ff","Gateway": "172.17.0.1","IPAddress": "172.17.0.2","IPPrefixLen": 16,"IPv6Gateway": "","GlobalIPv6Address": "","GlobalIPv6PrefixLen": 0,"MacAddress": "02:42:ac:11:00:02","DriverOpts": null}}}}
]

3、docker 容器,镜像建立流程图

4、docker 全部命令流程图

6、 docker 部署 nginx

1、下载nginx ,并运行nginx容器

docker search nginx# 注意 run 的镜像不存在,会自动下载
docker run -d --name nginx01 -p 6666:80 nginx-it 交互式进入容器
-d 后台方式允许
--name 容器命名
-p 向主机暴露端口   主机端口:容器端口
[root@VM-8-9-centos ~]# docker run -d --name nginx01 -p 6666:80 nginx
b2a09893d3ab15e83517cd30dd2bd6dd563fb126d9b9a014af6167e0cb1c4f8b
[root@VM-8-9-centos ~]# docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS                                   NAMES
b2a09893d3ab   nginx     "/docker-entrypoint.…"   23 seconds ago   Up 23 seconds   0.0.0.0:6666->80/tcp, :::6666->80/tcp   nginx01

2、测试nginx

[root@VM-8-9-centos ~]# curl localhost:6666
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>body {width: 35em;margin: 0 auto;font-family: Tahoma, Verdana, Arial, sans-serif;}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p><p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p><p><em>Thank you for using nginx.</em></p>
</body>
</html>

3、以一个新的终端方式交互式进入容器

# 查看当前正在运行的容器
[root@VM-8-9-centos ~]# docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS                                   NAMES
b2a09893d3ab   nginx     "/docker-entrypoint.…"   4 minutes ago   Up 4 minutes   0.0.0.0:6666->80/tcp, :::6666->80/tcp   nginx01# 进入容器
[root@VM-8-9-centos ~]# docker exec -it nginx01 /bin/bash
root@b2a09893d3ab:/# 操作容器。。。。

7、docker部署tomcat

1、下载镜像

docker pull tomcat9.0.48-jdk8-adoptopenjdk-openj9

2、查看下载的镜像

[root@VM-8-9-centos ~]# docker images
REPOSITORY   TAG                               IMAGE ID       CREATED        SIZE
tomcat       9.0.48-jdk8-adoptopenjdk-openj9   d716ed54947b   6 hours ago    386MB
nginx        latest                            d1a364dc548d   3 weeks ago    133MB
mysql        8.0.25                            c0cdc95609f1   5 weeks ago    556MB
centos       latest                            300e315adb2f   6 months ago   209MB

3、运行容器

[root@VM-8-9-centos ~]# docker run -d --name tomcat01 -p 6666:8080 d716ed54947b
9af883046bff06510901fe15b87cd0ea3134f43f91eedaf0019cb6b17df77932
[root@VM-8-9-centos ~]# docker ps
CONTAINER ID   IMAGE          COMMAND             CREATED          STATUS          PORTS                                       NAMES
9af883046bff   d716ed54947b   "catalina.sh run"   11 seconds ago   Up 10 seconds   0.0.0.0:6666->8080/tcp, :::6666->8080/tcp   tomcat01

4、查看容器日志

[root@VM-8-9-centos ~]# docker logs  --tail 1000 9af883046bff
17-Jun-2021 06:58:36.285 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version name:   Apache Tomcat/9.0.48
17-Jun-2021 06:58:36.286 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server built:          Jun 10 2021 09:22:01 UTC
17-Jun-2021 06:58:36.287 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version number: 9.0.48.0
17-Jun-2021 06:58:36.287 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Name:               Linux
17-Jun-2021 06:58:36.287 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Version:            3.10.0-1127.19.1.el7.x86_64
17-Jun-2021 06:58:36.287 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Architecture:          amd64
17-Jun-2021 06:58:36.287 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Java Home:             /opt/java/openjdk/jre
17-Jun-2021 06:58:36.287 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Version:           1.8.0_292-b10
17-Jun-2021 06:58:36.287 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Vendor:            Eclipse OpenJ9
17-Jun-2021 06:58:36.287 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_BASE:         /usr/local/tomcat
17-Jun-2021 06:58:36.288 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_HOME:         /usr/local/tomcat
17-Jun-2021 06:58:36.290 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Xoptionsfile=/opt/java/openjdk/jre/lib/amd64/default/options.default
17-Jun-2021 06:58:36.290 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Xlockword:mode=default,noLockword=java/lang/String,noLockword=java/util/MapEntry,noLockword=java/util/HashMap$Entry,noLockword=org/apache/harmony/luni/util/ModifiedMap$Entry,noLockword=java/util/Hashtable$Entry,noLockword=java/lang/invoke/MethodType,noLockword=java/lang/invoke/MethodHandle,noLockword=java/lang/invoke/CollectHandle,noLockword=java/lang/invoke/ConstructorHandle,noLockword=java/lang/invoke/ConvertHandle,noLockword=java/lang/invoke/ArgumentConversionHandle,noLockword=java/lang/invoke/AsTypeHandle,noLockword=java/lang/invoke/ExplicitCastHandle,noLockword=java/lang/invoke/FilterReturnHandle,noLockword=java/lang/invoke/DirectHandle,noLockword=java/lang/invoke/ReceiverBoundHandle,noLockword=java/lang/invoke/DynamicInvokerHandle,noLockword=java/lang/invoke/FieldHandle,noLockword=java/lang/invoke/FieldGetterHandle,noLockword=java/lang/invoke/FieldSetterHandle,noLockword=java/lang/invoke/StaticFieldGetterHandle,noLockword=java/lang/invoke/StaticFieldSetterHandle,noLockword=java/lang/invoke/IndirectHandle,noLockword=java/lang/invoke/InterfaceHandle,noLockword=java/lang/invoke/VirtualHandle,noLockword=java/lang/invoke/PrimitiveHandle,noLockword=java/lang/invoke/InvokeExactHandle,noLockword=java/lang/invoke/InvokeGenericHandle,noLockword=java/lang/invoke/VarargsCollectorHandle,noLockword=java/lang/invoke/ThunkTuple
17-Jun-2021 06:58:36.290 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Xjcl:jclse29
17-Jun-2021 06:58:36.291 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dcom.ibm.oti.vm.bootstrap.library.path=/opt/java/openjdk/jre/lib/amd64/default:/opt/java/openjdk/jre/lib/amd64
17-Jun-2021 06:58:36.291 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dsun.boot.library.path=/opt/java/openjdk/jre/lib/amd64/default:/opt/java/openjdk/jre/lib/amd64
17-Jun-2021 06:58:36.291 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.library.path=/opt/java/openjdk/jre/lib/amd64/default:/opt/java/openjdk/jre/lib/amd64:/usr/local/tomcat/native-jni-lib:/usr/lib64:/usr/lib
17-Jun-2021 06:58:36.291 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.home=/opt/java/openjdk/jre
17-Jun-2021 06:58:36.291 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.ext.dirs=/opt/java/openjdk/jre/lib/ext
17-Jun-2021 06:58:36.291 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Duser.dir=/usr/local/tomcat
17-Jun-2021 06:58:36.291 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -XX:+IgnoreUnrecognizedVMOptions
17-Jun-2021 06:58:36.292 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -XX:+IdleTuningGcOnIdle
17-Jun-2021 06:58:36.292 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Xshareclasses:name=openj9_system_scc,cacheDir=/opt/java/.scc,readonly,nonFatal
17-Jun-2021 06:58:36.292 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.class.path=.
17-Jun-2021 06:58:36.293 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties
17-Jun-2021 06:58:36.293 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
17-Jun-2021 06:58:36.293 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djdk.tls.ephemeralDHKeySize=2048
17-Jun-2021 06:58:36.293 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.protocol.handler.pkgs=org.apache.catalina.webresources
17-Jun-2021 06:58:36.293 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dorg.apache.catalina.security.SecurityListener.UMASK=0027
17-Jun-2021 06:58:36.293 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dignore.endorsed.dirs=
17-Jun-2021 06:58:36.293 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.class.path=/usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar
17-Jun-2021 06:58:36.293 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dcatalina.base=/usr/local/tomcat
17-Jun-2021 06:58:36.293 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dcatalina.home=/usr/local/tomcat
17-Jun-2021 06:58:36.294 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.io.tmpdir=/usr/local/tomcat/temp
17-Jun-2021 06:58:36.294 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dsun.java.command=org.apache.catalina.startup.Bootstrap start
17-Jun-2021 06:58:36.294 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dsun.java.launcher=SUN_STANDARD
17-Jun-2021 06:58:36.294 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dsun.java.launcher.pid=1
17-Jun-2021 06:58:36.297 INFO [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent Loaded Apache Tomcat Native library [1.2.30] using APR version [1.6.5].
17-Jun-2021 06:58:36.297 INFO [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true], UDS [true].
17-Jun-2021 06:58:36.297 INFO [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent APR/OpenSSL configuration: useAprConnector [false], useOpenSSL [true]
17-Jun-2021 06:58:36.299 INFO [main] org.apache.catalina.core.AprLifecycleListener.initializeSSL OpenSSL successfully initialized [OpenSSL 1.1.1f  31 Mar 2020]
17-Jun-2021 06:58:36.479 INFO [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["http-nio-8080"]
17-Jun-2021 06:58:36.491 INFO [main] org.apache.catalina.startup.Catalina.load Server initialization in [311] milliseconds
17-Jun-2021 06:58:36.510 INFO [main] org.apache.catalina.core.StandardService.startInternal Starting service [Catalina]
17-Jun-2021 06:58:36.510 INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet engine: [Apache Tomcat/9.0.48]
17-Jun-2021 06:58:36.516 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8080"]
17-Jun-2021 06:58:36.524 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [32] milliseconds

5、外部访问

进入容器 拷贝webapps.dist目录内容 到 webapps目录下即可

[root@VM-8-9-centos ~]# docker exec -it tomcat01 /bin/bash[root@VM-8-9-centos ~]#  cp -r ../webapps.dist/* ./

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eeFvHXRn-1624330672107)(C:\Users\Meet\AppData\Roaming\Typora\typora-user-images\image-20210618111208095.png)]

8、配置docker镜像加速

地址:https://cr.console.aliyun.com

1、创建镜像仓库

根据需求创建 个人版 还是 企业版

2、为自己服务器的docker配置镜像加速

# 创建docker配置目录
sudo mkdir -p /etc/docker
sudo vim /etc/docker/daemon.json
# 添加这一行
{"registry-mirrors": ["加速器地址"]
}
# 重启docker
sudo systemctl daemon-reload
sudo systemctl restart docker

9、Docker 核心技术与实现原理

原文链接:https://draveness.me/docker

提到虚拟化技术,我们首先想到的一定是 Docker,经过四年的快速发展 Docker 已经成为了很多公司的标配,也不再是一个只能在开发阶段使用的玩具了。作为在生产环境中广泛应用的产品,Docker 有着非常成熟的社区以及大量的使用者,代码库中的内容也变得非常庞大。

同样,由于项目的发展、功能的拆分以及各种奇怪的改名 PR,让我们再次理解 Docker 的的整体架构变得更加困难。

虽然 Docker 目前的组件较多,并且实现也非常复杂,但是本文不想过多的介绍 Docker 具体的实现细节,我们更想谈一谈 Docker 这种虚拟化技术的出现有哪些核心技术的支撑。

首先,Docker 的出现一定是因为目前的后端在开发和运维阶段确实需要一种虚拟化技术解决开发环境和生产环境环境一致的问题,通过 Docker 我们可以将程序运行的环境也纳入到版本控制中,排除因为环境造成不同运行结果的可能。但是上述需求虽然推动了虚拟化技术的产生,但是如果没有合适的底层技术支撑,那么我们仍然得不到一个完美的产品。本文剩下的内容会介绍几种 Docker 使用的核心技术,如果我们了解它们的使用方法和原理,就能清楚 Docker 的实现原理。

Namespaces

命名空间(namespaces)是 Linux 为我们提供的用于分离进程树、网络接口、挂载点以及进程间通信等资源的方法。在日常使用 Linux 或者 macOS 时,我们并没有运行多个完全分离的服务器的需要,但是如果我们在服务器上启动了多个服务,这些服务其实会相互影响的,每一个服务都能看到其他服务的进程,也可以访问宿主机器上的任意文件,这是很多时候我们都不愿意看到的,我们更希望运行在同一台机器上的不同服务能做到完全隔离,就像运行在多台不同的机器上一样。

在这种情况下,一旦服务器上的某一个服务被入侵,那么入侵者就能够访问当前机器上的所有服务和文件,这也是我们不想看到的,而 Docker 其实就通过 Linux 的 Namespaces 对不同的容器实现了隔离。

Linux 的命名空间机制提供了以下七种不同的命名空间,包括 CLONE_NEWCGROUP、CLONE_NEWIPC、CLONE_NEWNET、CLONE_NEWNS、CLONE_NEWPID、CLONE_NEWUSER 和 CLONE_NEWUTS,通过这七个选项我们能在创建新的进程时设置新进程应该在哪些资源上与宿主机器进行隔离。

进程

进程是 Linux 以及现在操作系统中非常重要的概念,它表示一个正在执行的程序,也是在现代分时系统中的一个任务单元。在每一个 *nix 的操作系统上,我们都能够通过 ps 命令打印出当前操作系统中正在执行的进程,比如在 Ubuntu 上,使用该命令就能得到以下的结果:

$ ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 Apr08 ?        00:00:09 /sbin/init
root         2     0  0 Apr08 ?        00:00:00 [kthreadd]
root         3     2  0 Apr08 ?        00:00:05 [ksoftirqd/0]
root         5     2  0 Apr08 ?        00:00:00 [kworker/0:0H]
root         7     2  0 Apr08 ?        00:07:10 [rcu_sched]
root        39     2  0 Apr08 ?        00:00:00 [migration/0]
root        40     2  0 Apr08 ?        00:01:54 [watchdog/0]
...

当前机器上有很多的进程正在执行,在上述进程中有两个非常特殊,一个是 pid 为 1 的 /sbin/init 进程,另一个是 pid 为 2 的 kthreadd 进程,这两个进程都是被 Linux 中的上帝进程 idle 创建出来的,其中前者负责执行内核的一部分初始化工作和系统配置,也会创建一些类似 getty 的注册进程,而后者负责管理和调度其他的内核进程。

如果我们在当前的 Linux 操作系统下运行一个新的 Docker 容器,并通过 exec 进入其内部的 bash 并打印其中的全部进程,我们会得到以下的结果:

root@iZ255w13cy6Z:~# docker run -it -d ubuntu
b809a2eb3630e64c581561b08ac46154878ff1c61c6519848b4a29d412215e79
root@iZ255w13cy6Z:~# docker exec -it b809a2eb3630 /bin/bash
root@b809a2eb3630:/# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 15:42 pts/0    00:00:00 /bin/bash
root         9     0  0 15:42 pts/1    00:00:00 /bin/bash
root        17     9  0 15:43 pts/1    00:00:00 ps -ef

在新的容器内部执行 ps 命令打印出了非常干净的进程列表,只有包含当前 ps -ef 在内的三个进程,在宿主机器上的几十个进程都已经消失不见了。

当前的 Docker 容器成功将容器内的进程与宿主机器中的进程隔离,如果我们在宿主机器上打印当前的全部进程时,会得到下面三条与 Docker 相关的结果:

UID        PID  PPID  C STIME TTY          TIME CMD
root     29407     1  0 Nov16 ?        00:08:38 /usr/bin/dockerd --raw-logs
root      1554 29407  0 Nov19 ?        00:03:28 docker-containerd -l unix:///var/run/docker/libcontainerd/docker-containerd.sock --metrics-interval=0 --start-timeout 2m --state-dir /var/run/docker/libcontainerd/containerd --shim docker-containerd-shim --runtime docker-runc
root      5006  1554  0 08:38 ?        00:00:00 docker-containerd-shim b809a2eb3630e64c581561b08ac46154878ff1c61c6519848b4a29d412215e79 /var/run/docker/libcontainerd/b809a2eb3630e64c581561b08ac46154878ff1c61c6519848b4a29d412215e79 docker-runc

在当前的宿主机器上,可能就存在由上述的不同进程构成的进程树:

这就是在使用 clone(2) 创建新进程时传入 CLONE_NEWPID 实现的,也就是使用 Linux 的命名空间实现进程的隔离,Docker 容器内部的任意进程都对宿主机器的进程一无所知。

containerRouter.postContainersStart
└── daemon.ContainerStart
└── daemon.createSpec└── setNamespaces└── setNamespace

Docker 的容器就是使用上述技术实现与宿主机器的进程隔离,当我们每次运行 docker run 或者 docker start 时,都会在下面的方法中创建一个用于设置进程间隔离的 Spec:

func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {s := oci.DefaultSpec()// ...
if err := setNamespaces(daemon, &s, c); err != nil {return nil, fmt.Errorf("linux spec namespaces: %v", err)
}return &s, nil
}

在 setNamespaces 方法中不仅会设置进程相关的命名空间,还会设置与用户、网络、IPC 以及 UTS 相关的命名空间:

func setNamespaces(daemon *Daemon, s *specs.Spec, c *container.Container) error {// user
// network
// ipc
// uts// pid
if c.HostConfig.PidMode.IsContainer() {ns := specs.LinuxNamespace{Type: "pid"}pc, err := daemon.getPidContainer(c)if err != nil {return err}ns.Path = fmt.Sprintf("/proc/%d/ns/pid", pc.State.GetPID())setNamespace(s, ns)
} else if c.HostConfig.PidMode.IsHost() {oci.RemoveNamespace(s, specs.LinuxNamespaceType("pid"))
} else {ns := specs.LinuxNamespace{Type: "pid"}setNamespace(s, ns)
}return nil
}

所有命名空间相关的设置 Spec 最后都会作为 Create 函数的入参在创建新的容器时进行设置:

daemon.containerd.Create(context.Background(), container.ID, spec, createOptions)

所有与命名空间的相关的设置都是在上述的两个函数中完成的,Docker 通过命名空间成功完成了与宿主机进程和网络的隔离。

网络

如果 Docker 的容器通过 Linux 的命名空间完成了与宿主机进程的网络隔离,但是却有没有办法通过宿主机的网络与整个互联网相连,就会产生很多限制,所以 Docker 虽然可以通过命名空间创建一个隔离的网络环境,但是 Docker 中的服务仍然需要与外界相连才能发挥作用。
每一个使用 docker run 启动的容器其实都具有单独的网络命名空间(默认使用的是docker0网桥),Docker 为我们提供了四种不同的网络模式,Host、Container、None 和 Bridge 模式。

在这一部分,我们将介绍 Docker 默认的网络设置模式:网桥模式。在这种模式下,除了分配隔离的网络命名空间之外,Docker 还会为所有的容器设置 IP 地址。当 Docker 服务器在主机上启动之后会创建新的虚拟网桥 docker0,随后在该主机上启动的全部服务在默认情况下都与该网桥相连。

在默认情况下,每一个容器在创建时都会创建一对虚拟网卡(veth part),两个虚拟网卡组成了数据的通道,其中一个会放在创建的容器中,会加入到名为 docker0 网桥中。我们可以使用如下的命令来查看当前网桥的接口:

$ brctl show
bridge name bridge id       STP enabled interfaces
docker0     8000.0242a6654980   no      veth3e84d4fveth9953b75

docker0 会为每一个容器分配一个新的 IP 地址并将 docker0 的 IP 地址设置为默认的网关。网桥 docker0 通过 iptables 中的配置与宿主机器上的网卡相连,所有符合条件的请求都会通过 iptables 转发到 docker0 并由网桥分发给对应的机器。

# 注意centos7 是没有iptables的
$ iptables -t nat -L
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  anywhere             anywhere             ADDRTYPE match dst-type LOCALChain DOCKER (2 references)
target     prot opt source               destination
RETURN     all  --  anywhere             anywhere

我们在当前的机器上使用 docker run -d -p 6379:6379 redis 命令启动了一个新的 Redis 容器,在这之后我们再查看当前 iptables 的 NAT 配置就会看到在 DOCKER 的链中出现了一条新的规则:

DNAT       tcp  --  anywhere             anywhere             tcp dpt:6379 to:192.168.0.4:6379

上述规则会将从任意源发送到当前机器 6379 端口的 TCP 包转发到 192.168.0.4:6379 所在的地址上。

这个地址其实也是 Docker 为 Redis 服务分配的 IP 地址,如果我们在当前机器上直接 ping 这个 IP 地址就会发现它是可以访问到的:

$ ping 192.168.0.4
PING 192.168.0.4 (192.168.0.4) 56(84) bytes of data.
64 bytes from 192.168.0.4: icmp_seq=1 ttl=64 time=0.069 ms
64 bytes from 192.168.0.4: icmp_seq=2 ttl=64 time=0.043 ms
^C
--- 192.168.0.4 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.043/0.056/0.069/0.013 ms

从上述的一系列现象,我们就可以推测出 Docker 是如何将容器的内部的端口暴露出来并对数据包进行转发的了;当有 Docker 的容器需要将服务暴露给宿主机器,就会为容器分配一个 IP 地址,同时向 iptables 中追加一条新的规则 linux主机端口:容器端口。

当我们使用 redis-cli 在宿主机器的命令行中访问 127.0.0.1:6379 的地址时,经过 iptables 的 NAT PREROUTING 将 ip 地址定向到了 192.168.0.4,重定向过的数据包就可以通过 iptables 中的 FILTER 配置,最终在 NAT POSTROUTING 阶段将 ip 地址伪装成 127.0.0.1,到这里虽然从外面看起来我们请求的是 127.0.0.1:6379,但是实际上请求的已经是 Docker 容器暴露出的端口了。

$ redis-cli -h 127.0.0.1 -p 6379 ping
PONG

Docker 通过 Linux 的命名空间实现了网络的隔离,又通过 iptables 进行数据包转发,让 Docker 容器能够优雅地为宿主机器或者其他容器提供服务。

Libnetwork

整个网络部分的功能都是通过 Docker 拆分出来的 libnetwork 实现的,它提供了一个连接不同容器的实现,同时也能够为应用给出一个能够提供一致的编程接口和网络层抽象的容器网络模型。

The goal of libnetwork is to deliver a robust Container Network Model that provides a consistent programming interface and the required network abstractions for applications.

libnetwork 中最重要的概念,容器网络模型由以下的几个主要组件组成,分别是 Sandbox、Endpoint 和 Network:

在容器网络模型中,每一个容器内部都包含一个 Sandbox,其中存储着当前容器的网络栈配置,包括容器的接口、路由表和 DNS 设置,Linux 使用网络命名空间实现这个 Sandbox,每一个 Sandbox 中都可能会有一个或多个 Endpoint,在 Linux 上就是一个虚拟的网卡 veth,Sandbox 通过 Endpoint 加入到对应的网络中,这里的网络可能就是我们在上面提到的 Linux 网桥或者 VLAN。

想要获得更多与 libnetwork 或者容器网络模型相关的信息,可以阅读 Design · libnetwork 了解更多信息,当然也可以阅读源代码了解不同 OS 对容器网络模型的不同实现。

挂载点

虽然我们已经通过 Linux 的命名空间解决了进程和网络隔离的问题,在 Docker 进程中我们已经没有办法访问宿主机器上的其他进程并且限制了网络的访问,但是 Docker 容器中的进程仍然能够访问或者修改宿主机器上的其他目录,这是我们不希望看到的。

在新的进程中创建隔离的挂载点命名空间需要在 clone 函数中传入 CLONE_NEWNS,这样子进程就能得到父进程挂载点的拷贝,如果不传入这个参数子进程对文件系统的读写都会同步回父进程以及整个主机的文件系统。

如果一个容器需要启动,那么它一定需要提供一个根文件系统(rootfs),容器需要使用这个文件系统来创建一个新的进程,所有二进制的执行都必须在这个根文件系统中。

想要正常启动一个容器就需要在 rootfs 中挂载以上的几个特定的目录,除了上述的几个目录需要挂载之外我们还需要建立一些符号链接保证系统 IO 不会出现问题。

为了保证当前的容器进程没有办法访问宿主机器上其他目录,我们在这里还需要通过 libcotainer 提供的 pivor_root 或者 chroot 函数改变进程能够访问个文件目录的根节点。

// pivor_root
put_old = mkdir(...);
pivot_root(rootfs, put_old);
chdir("/");
unmount(put_old, MS_DETACH);
rmdir(put_old);// chroot
mount(rootfs, "/", NULL, MS_MOVE, NULL);
chroot(".");
chdir("/");

到这里我们就将容器需要的目录挂载到了容器中,同时也禁止当前的容器进程访问宿主机器上的其他目录,保证了不同文件系统的隔离。

这一部分的内容是作者在 libcontainer 中的 SPEC.md 文件中找到的,其中包含了 Docker 使用的文件系统的说明,对于 Docker 是否真的使用 chroot 来确保当前的进程无法访问宿主机器的目录,作者其实也没有确切的答案,一是 Docker 项目的代码太多庞大,不知道该从何入手,作者尝试通过 Google 查找相关的结果,但是既找到了无人回答的问题,也得到了与 SPEC 中的描述有冲突的答案,如果各位读者有明确的答案可以在博客下面留言,非常感谢。

Chroot

在这里不得不简单介绍一下 chroot(change root),在 Linux 系统中,系统默认的目录就都是以 / 也就是根目录开头的,chroot 的使用能够改变当前的系统根目录结构,通过改变当前系统的根目录,我们能够限制用户的权利,在新的根目录下并不能够访问旧系统根目录的结构个文件,也就建立了一个与原系统完全隔离的目录结构。

与 chroot 的相关内容部分来自《理解 chroot》一文,各位读者可以阅读这篇文章获得更详细的信息。

CGroups

我们通过 Linux 的命名空间为新创建的进程隔离了文件系统、网络并与宿主机器之间的进程相互隔离,但是命名空间并不能够为我们提供物理资源上的隔离,比如 CPU 或者内存,如果在同一台机器上运行了多个对彼此以及宿主机器一无所知的『容器』,这些容器却共同占用了宿主机器的物理资源。

如果其中的某一个容器正在执行 CPU 密集型的任务,那么就会影响其他容器中任务的性能与执行效率,导致多个容器相互影响并且抢占资源。如何对多个容器的资源使用进行限制就成了解决进程虚拟资源隔离之后的主要问题,而 Control Groups(简称 CGroups)就是能够隔离宿主机器上的物理资源,例如 CPU、内存、磁盘 I/O 和网络带宽。

每一个 CGroup 都是一组被相同的标准和参数限制的进程,不同的 CGroup 之间是有层级关系的,也就是说它们之间可以从父类继承一些用于限制资源使用的标准和参数。

Linux 的 CGroup 能够为一组进程分配资源,也就是我们在上面提到的 CPU、内存、网络带宽等资源,通过对资源的分配,CGroup 能够提供以下的几种功能:

在 CGroup 中,所有的任务就是一个系统的一个进程,而 CGroup 就是一组按照某种标准划分的进程,在 CGroup 这种机制中,所有的资源控制都是以 CGroup 作为单位实现的,每一个进程都可以随时加入一个 CGroup 也可以随时退出一个 CGroup。
——CGroup 介绍、应用实例及原理描述

Linux 使用文件系统来实现 CGroup,我们可以直接使用下面的命令查看当前的 CGroup 中有哪些子系统:

$ lssubsys -m
cpuset /sys/fs/cgroup/cpuset
cpu /sys/fs/cgroup/cpu
cpuacct /sys/fs/cgroup/cpuacct
memory /sys/fs/cgroup/memory
devices /sys/fs/cgroup/devices
freezer /sys/fs/cgroup/freezer
blkio /sys/fs/cgroup/blkio
perf_event /sys/fs/cgroup/perf_event
hugetlb /sys/fs/cgroup/hugetlb

大多数 Linux 的发行版都有着非常相似的子系统,而之所以将上面的 cpuset、cpu 等东西称作子系统,是因为它们能够为对应的控制组分配资源并限制资源的使用。

如果我们想要创建一个新的 cgroup 只需要在想要分配或者限制资源的子系统下面创建一个新的文件夹,然后这个文件夹下就会自动出现很多的内容,如果你在 Linux 上安装了 Docker,你就会发现所有子系统的目录下都有一个名为 Docker 的文件夹:

$ ls cpu
cgroup.clone_children
...
cpu.stat
docker
notify_on_release
release_agent
tasks$ ls cpu/docker/
9c3057f1291b53fd54a3d12023d2644efe6a7db6ddf330436ae73ac92d401cf1
cgroup.clone_children
...
cpu.stat
notify_on_release
release_agent
tasks

9c3057xxx 其实就是我们运行的一个 Docker 容器,启动这个容器时,Docker 会为这个容器创建一个与容器标识符相同的 CGroup,在当前的主机上 CGroup 就会有以下的层级关系:

每一个 CGroup 下面都有一个 tasks 文件,其中存储着属于当前控制组的所有进程的 pid,作为负责 cpu 的子系统,cpu.cfs_quota_us 文件中的内容能够对 CPU 的使用作出限制,如果当前文件的内容为 50000,那么当前控制组中的全部进程的 CPU 占用率不能超过 50%。

如果系统管理员想要控制 Docker 某个容器的资源使用率就可以在 docker 这个父控制组下面找到对应的子控制组并且改变它们对应文件的内容,当然我们也可以直接在程序运行时就使用参数,让 Docker 进程去改变相应文件中的内容。

$ docker run -it -d --cpu-quota=50000 busybox
53861305258ecdd7f5d2a3240af694aec9adb91cd4c7e210b757f71153cdd274
$ cd 53861305258ecdd7f5d2a3240af694aec9adb91cd4c7e210b757f71153cdd274/
$ ls
cgroup.clone_children  cgroup.event_control  cgroup.procs  cpu.cfs_period_us  cpu.cfs_quota_us  cpu.shares  cpu.stat  notify_on_release  tasks
$ cat cpu.cfs_quota_us
50000

当我们使用 Docker 关闭掉正在运行的容器时,Docker 的子控制组对应的文件夹也会被 Docker 进程移除,Docker 在使用 CGroup 时其实也只是做了一些创建文件夹改变文件内容的文件操作,不过 CGroup 的使用也确实解决了我们限制子容器资源占用的问题,系统管理员能够为多个容器合理的分配资源并且不会出现多个容器互相抢占资源的问题。

UnionFS

Linux 的命名空间和控制组分别解决了不同资源隔离的问题,前者解决了进程、网络以及文件系统的隔离,后者实现了 CPU、内存等资源的隔离,但是在 Docker 中还有另一个非常重要的问题需要解决 - 也就是镜像。

镜像到底是什么,它又是如何组成和组织的是作者使用 Docker 以来的一段时间内一直比较让作者感到困惑的问题,我们可以使用 docker run 非常轻松地从远程下载 Docker 的镜像并在本地运行。

Docker 镜像其实本质就是一个压缩包,我们可以使用下面的命令将一个 Docker 镜像中的文件导出:

$ docker export $(docker create busybox) | tar -C rootfs -xvf -
$ ls
bin  dev  etc  home proc root sys  tmp  usr  var

你可以看到这个 busybox 镜像中的目录结构与 Linux 操作系统的根目录中的内容并没有太多的区别,可以说 Docker 镜像就是一个文件。

存储驱动

Docker 使用了一系列不同的存储驱动管理镜像内的文件系统并运行容器,这些存储驱动与 Docker 卷(volume)有些不同,存储引擎管理着能够在多个容器之间共享的存储。

想要理解 Docker 使用的存储驱动,我们首先需要理解 Docker 是如何构建并且存储镜像的,也需要明白 Docker 的镜像是如何被每一个容器所使用的;Docker 中的每一个镜像都是由一系列只读的层组成的,Dockerfile 中的每一个命令都会在已有的只读层上创建一个新的层:

FROM ubuntu:15.04
COPY . /app
RUN make /app
CMD python /app/app.py

容器中的每一层都只对当前容器进行了非常小的修改,上述的 Dockerfile 文件会构建一个拥有四层 layer 的镜像:

当镜像被 docker run 命令创建时就会在镜像的最上层添加一个可写的层,也就是容器层,所有对于运行时容器的修改其实都是对这个容器读写层的修改。

容器和镜像的区别就在于,所有的镜像都是只读的,而每一个容器其实等于镜像加上一个可读写的层,也就是同一个镜像可以对应多个容器。

AUFS

UnionFS 其实是一种为 Linux 操作系统设计的用于把多个文件系统『联合』到同一个挂载点的文件系统服务。而 AUFS 即 Advanced UnionFS 其实就是 UnionFS 的升级版,它能够提供更优秀的性能和效率。

AUFS 作为联合文件系统,它能够将不同文件夹中的层联合(Union)到了同一个文件夹中,这些文件夹在 AUFS 中称作分支,整个『联合』的过程被称为联合挂载(Union Mount):

每一个镜像层或者容器层都是 /var/lib/docker/ 目录下的一个子文件夹;在 Docker 中,所有镜像层和容器层的内容都存储在 /var/lib/docker/aufs/diff/ 目录中:

$ ls /var/lib/docker/aufs/diff/00adcccc1a55a36a610a6ebb3e07cc35577f2f5a3b671be3dbc0e74db9ca691c       93604f232a831b22aeb372d5b11af8c8779feb96590a6dc36a80140e38e764d8
00adcccc1a55a36a610a6ebb3e07cc35577f2f5a3b671be3dbc0e74db9ca691c-init  93604f232a831b22aeb372d5b11af8c8779feb96590a6dc36a80140e38e764d8-init
019a8283e2ff6fca8d0a07884c78b41662979f848190f0658813bb6a9a464a90       93b06191602b7934fafc984fbacae02911b579769d0debd89cf2a032e7f35cfa
...

而 /var/lib/docker/aufs/layers/ 中存储着镜像层的元数据,每一个文件都保存着镜像层的元数据,最后的 /var/lib/docker/aufs/mnt/ 包含镜像或者容器层的挂载点,最终会被 Docker 通过联合的方式进行组装。

上面的这张图片非常好的展示了组装的过程,每一个镜像层都是建立在另一个镜像层之上的,同时所有的镜像层都是只读的,只有每个容器最顶层的容器层才可以被用户直接读写,所有的容器都建立在一些底层服务(Kernel)上,包括命名空间、控制组、rootfs 等等,这种容器的组装方式提供了非常大的灵活性,只读的镜像层通过共享也能够减少磁盘的占用。

其他存储驱动

AUFS 只是 Docker 使用的存储驱动的一种,除了 AUFS 之外,Docker 还支持了不同的存储驱动,包括 aufs、devicemapper、overlay2、zfs 和 vfs 等等,在最新的 Docker 中,overlay2 取代了 aufs 成为了推荐的存储驱动,但是在没有 overlay2 驱动的机器上仍然会使用 aufs 作为 Docker 的默认驱动。

不同的存储驱动在存储镜像和容器文件时也有着完全不同的实现,有兴趣的读者可以在 Docker 的官方文档 Select a storage driver 中找到相应的内容。

想要查看当前系统的 Docker 上使用了哪种存储驱动只需要使用以下的命令就能得到相对应的信息:

$ docker info | grep Storage
Storage Driver: aufs

作者的这台 Ubuntu 上由于没有 overlay2 存储驱动,所以使用 aufs 作为 Docker 的默认存储驱动。

总结

Docker 目前已经成为了非常主流的技术,已经在很多成熟公司的生产环境中使用,但是 Docker 的核心技术其实已经有很多年的历史了,Linux 命名空间、控制组和 UnionFS 三大技术支撑了目前 Docker 的实现,也是 Docker 能够出现的最重要原因。

作者在学习 Docker 实现原理的过程中查阅了非常多的资料,从中也学习到了很多与 Linux 操作系统相关的知识,不过由于 Docker 目前的代码库实在是太过庞大,想要从源代码的角度完全理解 Docker 实现的细节已经是非常困难的了,但是如果各位读者真的对其实现细节感兴趣,可以从 Docker CE 的源代码开始了解 Docker 的原理。

10、docker镜像原理

文件系统

docker的镜像是由多个只读的文件系统叠加在一起形成的。当我们在我启动一个容器的时候,docker会加载这些只读层并在这些只读层的上面(栈顶)增加一个读写层。这时如果修改正在运行的容器中已有的文件,那么这个文件将会从只读层复制到读写层。该文件的只读版本还在,只是被上面读写层的该文件的副本隐藏。当删除docker,或者重新启动时,之前的更改将会消失。在Docker中,只读层及在顶部的读写层的组合被称为Union File System(联合文件系统)。

1、base 镜像(linux 的 docker镜像)

base 镜像有两层含义:

1. 不依赖其他镜像,从 scratch 构建。
2. 其他镜像可以之为基础进行扩展。

base 镜像的通常都是各种 Linux 发行版的 Docker 镜像,比如 Ubuntu, Debian, CentOS 等,以 CentOS 为例学习 base 镜像包含哪些内容。

这种docker镜像往往比较小,才两百多M,不想自己下载的那种一两G

在这里插入图片描述

那么,为什么linux镜像版 这么小呢?

Linux 操作系统由内核空间和用户空间组成。

rootfs

内核空间是 kernel,Linux 刚启动时会加载 bootfs 文件系统,之后 bootfs 会被卸载掉。
用户空间的文件系统是 rootfs,包含我们熟悉的 /dev, /proc, /bin 等目录。

对于 base 镜像来说,底层直接用 Host(服务器) 的 kernel,自己只需要提供 rootfs 就行了。

而对于一个精简的 OS,rootfs 可以很小,只需要包括最基本的命令、工具和程序库就可以了。相比其他 Linux 发行版,CentOS 的 rootfs 很大,因为包含很多软件。

[root@VM-8-9-centos ~]# docker run -it --name centos1 centos
[root@dca312adb8a9 /]# uname -r
3.10.0-1127.19.1.el7.x86_64         容器内核kernel版本
[root@dca312adb8a9 /]# cat /etc/redhat-release
CentOS Linux release 8.3.2011
[root@dca312adb8a9 /]# exit
exit
[root@VM-8-9-centos ~]# uname -r
3.10.0-1127.19.1.el7.x86_64         linux内核kernel版本   基本一致

linux由bootfs和rootfs 组成 ,容器使用的是 linux服务器的kernel。所有容器都是共用host kernel。而容器只需要携带自生的rootfs文件系统。因此比较小

2、镜像的分层结构

Docker 支持通过扩展现有镜像,创建新的镜像。
实际上,Docker Hub 中 99% 的镜像都是通过在 base 镜像中安装和配置需要的软件构建出来的。

① 新镜像不再是从 scratch 开始,而是直接在 Debian base 镜像上构建。

② 安装 emacs 编辑器。
③ 安装 apache2。
④ 容器启动时运行 bash。

构建过程如下图所示:

可以看到,新镜像是从 base 镜像一层一层叠加生成的。每安装一个软件,就在现有镜像的基础上增加一层。

问什么 Docker 镜像要采用这种分层结构呢?
最大的一个好处就是 - 共享资源。

比如:有多个镜像都从相同的 base 镜像构建而来,那么 Docker Host 只需在磁盘上保存一份 base 镜像;同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。

这时可能就有人会问了:如果多个容器共享一份基础镜像,当某个容器修改了基础镜像的内容,比如 /etc 下的文件,这时其他容器的 /etc 是否也会被修改?
答案:不会!因为修改会被限制在单个容器内。

这就是我们接下来要学习的容器 Copy-on-Write 特性。

3、容器的可写层(Copy-on-Write 类似java的写时复制,并发技术)

当容器启动时,一个新的可写层被加载到镜像的顶部。
这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。

所有对容器的改动 - 无论添加、删除、还是修改文件都只会发生在容器层中。
只有容器层是可写的,容器层下面的所有镜像层都是只读的。
下面我们深入讨论容器层的细节。
镜像层数量可能会很多,所有镜像层会联合在一起组成一个统一的文件系统。如果不同层中有一个相同路径的文件,比如 /a,上层的 /a 会覆盖下层的 /a,也就是说用户只能访问到上层中的文件 /a。在容器层中,用户看到的是一个叠加之后的文件系统。

1、添加文件
在容器中创建文件时,新文件被添加到容器层中。
2、读取文件
在容器中读取某个文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,打开并读入内存。
3、 修改文件
在容器中修改已存在的文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,立即将其复制到容器层,然后修改之。
4、 删除文件
在容器中删除文件时,Docker 也是从上往下依次在镜像层中查找此文件。找到后,会在容器层中记录下此删除操作。

只有当需要修改时才复制一份数据,这种特性被称作 Copy-on-Write。可见,容器层保存的是镜像变化的部分,不会对镜像本身进行任何修改。
这样就解释了我们前面提出的问题:容器层记录对镜像的修改,所有镜像层都是只读的,不会被容器修改,所以镜像可以被多个容器共享。

11、docker数据卷

1、什么是docker数据卷

docker 数据卷呈现给docker容器的一个形式就是目录,该目录支持多个容器间共享,修改不会影响到镜像。便于数据共享和重用

2、为什么要使用数据卷

1、当创建一个容器的时候,容器运行,数据能不能持久化?

2、如果能够持久化,数据存储在哪?由于docker是隔离的,数据能不能存储在容器外?

3、如果部署很多容器,每次都需要进入容器中进行配置嘛?能不能外部进行配置

docker 数据卷呈现给docker容器的一个形式就是目录,该目录支持多个容器间共享,修改不会影响到镜像。使用Docker的数据卷,类似在系统中使用 mount 挂载一个文件系统。

Volume的作用

  • 通过数据卷可以在容器之间实现共享和重用
  • 对数据卷的修改会立马生效(非常适合作为开发环境)
  • 对数据卷的更新,不会影响镜像
  • 卷会一直存在,直到没有容器使用

3、使用docker 数据卷 和 数据卷容器

1、为容器 挂载数据卷

docker run用来创建容器,可以在使用改命令时添加-v参数,就可以创建并挂载一个到多个数据卷到当前运行的容器中,

-v的作用是将宿主机的一个目录作为容器的数据卷挂载到容器中,使宿主机和容器之间可以共享一个目录,如果本地路径不存在,Docker也会自动创建。

docker run -p 8080:80 -d --name nginx01 -v /home/test:/home/test   nginx

意思是: 我们创建了一个名称为nginx01的容器,将本机的9999端口映射到容器中nginx服务器的默认web访问端口80下,创建一个数据卷,并挂载到容器的/home/test目录下(注意:如果不存在会自动创建)。

我们可以通过docker inspect指令找到Volume在主机上的存储位置

docker inspect  nginx01

测试:往容器卷写入数据,查看linux主机是否,会自动同步共享

# 容器中
root@dda40c3f4e1a:/home/test# touch nginxVolumn.txt
root@dda40c3f4e1a:/home/test# ls
nginxVolumn.txt#主机中
[root@VM-8-9-centos test]# ls
nginxVolumn.txt# 容器数据卷 数据同步成功!

测试:当容器关闭的时候,linux往docker数据卷中写入数据,数据是否同步

# 关闭容器
[root@VM-8-9-centos ~]# docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS                                   NAMES
dda40c3f4e1a   nginx     "/docker-entrypoint.…"   12 minutes ago   Up 12 minutes   0.0.0.0:9999->80/tcp, :::9999->80/tcp   nginx01
[root@VM-8-9-centos ~]# docker stop nginx01 # linux往数据卷写入数据
[root@VM-8-9-centos test]# vim nginxVolumn.txt
linux update  in container close.# 开启容器 查看数据卷
[root@VM-8-9-centos ~]# docker start  nginx01
nginx01
[root@VM-8-9-centos ~]# docker exec -it nginx01 /bin/bash
root@dda40c3f4e1a:/# cd /home/test/
root@dda40c3f4e1a:/home/test# ls
nginxVolumn.txt
root@dda40c3f4e1a:/home/test# cat nginxVolumn.txt
linux update  in container close.#  即使容器关闭,该容器数据卷依旧存在 ,  linux往容器数据卷写入数据成功!

2、数据卷权限 挂载目录到容器中

挂载的数据卷默认为可读写权限,除非外部文件系统做了特殊限制,在docker run的时候也可以执行为只读权限

[root@VM-8-9-centos ~]# docker run -d -p 9999:80 --name nginx02 -v /home/authority:/home/authority:ro nginx# 容器中
root@e1ae8ee5c387:/home/authority# touch authority.txt
touch: cannot touch 'authority.txt': Read-only file system# linxu中
[root@VM-8-9-centos authority]# touch authority.txt
[root@VM-8-9-centos authority]# ls
authority.txt

ro: 的意义在于,规定 容器对/home/authority 数据卷 只拥有readonly的权限

但是 linux跟跟容器共享的数据卷/home/authority 是拥有可读可写的权限。

3、挂载宿主机上的文件 到 容器中

想让所有的容器都可以共享宿主机的/home/hostFile.txt,从而只需要改变宿主机的文件源就能够影响到所有的容器。

[root@VM-8-9-centos home]# docker run -it -d --name nginx03 -v /home/hostFile.txt:/home/hostFile.txt:ro nginx

4、数据卷容器

数据卷容器: 在多个容器中共享数据的容器(实际上就是共享一个目录)

如果需要在多个容器间共享数据,并希望永久保存这些数据,最好的方式是使用数据卷容器,类似于一个提供网络文件共享服务的NFS服务器。

数据卷容器创建方法跟普通容器一样,只需要指定宿主机的一个文件夹作为数据卷即可,使用docker create命令创建但不启动数据卷容器:

# 通过宿主机的文件目录  创建 数据卷容器
# 创建一个数据卷容器
[root@VM-8-9-centos ~]# docker create -v /home/shareDirecotry --name shareDataVolumnContainer centos /bin/true
4092c64c2bf26fa1de45e46ff8d0c023f77a82f251d54d44d30078d71fdce82a# 查看运行的容器
[root@VM-8-9-centos ~]# docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS     NAMES
c694a1581c35   nginx     "/docker-entrypoint.…"   47 minutes ago   Up 47 minutes   80/tcp    nginx03# 查看全部容器
[root@VM-8-9-centos ~]# docker ps -a
CONTAINER ID   IMAGE          COMMAND                  CREATED             STATUS                         PORTS     NAMES
# 共享的数据卷容器
4092c64c2bf2   centos         "/bin/true"              13 seconds ago      Created                                  shareDataVolumnContainer
c694a1581c35   nginx          "/docker-entrypoint.…"   47 minutes ago      Up 47 minutes                  80/tcp    nginx03
e1ae8ee5c387   nginx          "/docker-entrypoint.…"   About an hour ago   Exited (0) 51 minutes ago                nginx02
dda40c3f4e1a   nginx          "/docker-entrypoint.…"   About an hour ago   Exited (0) About an hour ago             nginx01
dca312adb8a9   centos         "/bin/bash"              5 hours ago         Exited (0) 5 hours ago                   centos1
c4930882a1f1   d716ed54947b   "catalina.sh run"        25 hours ago        Exited (143) 2 hours ago                 tomcat0# 创建多个容器 共享数据卷容器
[root@VM-8-9-centos ~]# docker run -it -d --volumes-from shareDataVolumnContainer  --name site1  nginx
9733bec6d7e6b860c5a6d39e229053faa94e48f59a9e843fb913def5e6f0e676
[root@VM-8-9-centos ~]# docker run -it -d --volumes-from shareDataVolumnContainer  --name site2  nginx
3926c6f338065c0e0764a576b286c3831821d34f1a919bdb5d3de6bd9979d303--volumes-from : 指定共享的数据卷容器

测试:多个容器共享数据卷容器

可以看到容器site1 在共享数据卷容器中创建了 site1.txt

容器site2 在共享数据卷容器中可以共享数据site1.txt

注意:此时linux主机是不能共享shareDirectory的

5、备份数据卷

**我们对数据卷容器中的数据进行备份,**备份方法:

1.创建一个新的容器
2.挂载数据卷容器
3.挂载宿主机本地目录作为数据卷
4.将数据卷容器的内容备份到宿主机本地目录挂载的数据卷中
5.完成备份操作后容器销毁

请按照上述步骤对数据卷容器shiyanloudb中的数据进行备份:

# 创建备份目录
mkdir /tmp/backup# 创建备份容器
docker run --rm --volumes-from shiyanloudb -v /tmp/backup:/backup ubuntu tar cvf /backup/shiyanloudb.tar /shiyanloudata

6、使用dockerFile 添加volume

使用VOLUME指令向容器添加volumeVOLUME /data

多个时VOLUME ["/data1","/data2"]

这种情况和第一个中情况docker run -v /data是一样的。注意,dockerfile中使用volume是不能和第二种方法那样挂载宿主机中指定的文件夹。这时为了保证Dockerfile的可移植性,因为不能保证所有的宿主机都有对应的文件夹。

需要注意的是,在Dockerfile中使用VOLUME指令后,如果尝试对这个volume进行修改,这些修改指令都不会生效,比如下面例子,尝试添加一个文件,并修改文件并改变文件所有权限

FROM test/mycent:v1.0
RUN useradd foo
VOLUME /data
RUN touch /data/x
RUN chown -R foo:foo /data
通过该Dockerfile创建镜像并启动容器后,该容器中存在用户foo,并且能看到在/data挂载的volume,但是/data文件夹的所有者并没有被改变为foo,而且/data下也没有/data/x文件。但是如果顺序反过来,先建文件,先授权,再挂载volume,那就得到期待的结果。

7、docker 具名,和匿名挂载

一、匿名挂载(指定容器数据卷,默认挂载到宿主机指定目录下生成的目录)

1.启动一个nginx 容器

语法格式:

-v   容器内路径!

[root@VM-8-9-centos ~]# docker run -d -p 9999:80 --name anonymousMount -v /etc/nginx  nginx

2.查看所有 volume 的情况

docker volume ls

该容器中数据卷会对应宿主机实际目录,通过docker inspect containerID 查看

docker inspect anonymousMount# 一般都位于/var/lib/docker/volumes/ 下"Mounts": [{"Type": "volume","Name": "9677f2ed4e4cd6ef93254f34faabbb037b37b0a46e4221bd411911b7c2715442","Source": "/var/lib/docker/volumes/9677f2ed4e4cd6ef93254f34faabbb037b37b0a46e4221bd411911b7c2715442/_data","Destination": "/etc/nginx","Driver": "local","Mode": "","RW": true,"Propagation": ""}],

3.这里发现,这种就是匿名挂载,在 -v 的时候只加容器内路径,没有加容器外路径!

二、具名挂载

1.重新启动一个nginx 容器

语法格式:

-v 卷名:容器内路径

注:卷名前面不加路径/

 docker run -d -P --name nginx02 -v slotMount:/etc/nginx nginx

2.查看所有 volume 的情况

docker volume ls
DRIVER              VOLUME NAME
local               4dcdd5b0a408438d90683df372cbc31f0fc48497c4252f61cf9d9c2a03747c01
local               slotMount

3.查看具名卷的信息

docker volume inspect slotMount
[{"CreatedAt": "2021-01-27T15:54:14+08:00","Driver": "local","Labels": null,"Mountpoint": "/var/lib/docker/volumes/juming-nginx/_data","Name": "juming-nginx","Options": null,"Scope": "local"}
]

所有docker容器内的卷,没有指定目录的情况下,都保存在“/var/lib/docker/volumes/xxx/_data”目录下。 # xxx为启动容器时设置的卷名

# 进入具名挂载的目录查看卷内的数据,可以看到nginx的配置文件存在!

[root@yang ~]# cd /var/lib/docker/volumes/slotMount/_data/
[root@yang _data]# ls
conf.d  fastcgi_params  koi-utf  koi-win  mime.types  modules  nginx.conf  scgi_params  uwsgi_params  win-utf

!!!具名挂载可以方便找到一个卷,大多数情况下,推荐使用【具名挂载】

三、如何确定是匿名挂载,具名挂载,或者是指定路径挂载?

-v   容器内路径          # 匿名挂载         /var/lib/docker/volumes  生成对应挂载目录
-v   卷名:容器内路径       # 具名挂载         /var/lib/docker/volumes  生成对应挂载目录
-v   / 宿主机目录:容器内目录   # 指定路径挂载

12、dockerfile理解和使用

dockerfile构建镜像

Dockerfile构建镜像是以基础镜像为基础的,**Dockerfile是一个文本文件,**内容是用户编写的一些docker指令,每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。

Dockerfile的基本指令有十三个,分别是:FROM、MAINTAINER、RUN、CMD、EXPOSE、ENV、ADD、COPY、ENTRYPOINT、VOLUME、USER、WORKDIR、ONBUILD

1、Dockerfile常用指令

类型 命令
基础镜像信息 FROM
维护者信息 MAINTAINER
镜像操作指令 RUN、COPY、ADD、EXPOSE、WORKDIR、ONBUILD、USER、VOLUME等
容器启动时执行指令 CMD、ENTRYPOINT

1.1、FROM :指定基础镜像

所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。就像我们之前运行了一个nginx镜像的容器,再进行修改一样,基础镜像是必须指定的。而FROM就是指定基础镜 像,因此一个Dockerfile中FROM是必备的指令,并且必须是第一条指令。
如:指定centos作为基础镜像

FROM centos

1.2、 RUN:执行命令

RUN指令:在新镜像内部执行的命令,如:执行某些动作、安装系统软件、配置系统信息之类,

格式如下两种:

1)shell格式:RUN< command > ,就像直接在命令行中输入的命令一样。

如在nginx里的默认主页中写”hello“:

RUN echo 'hello ' >/etc/nginx/html/index.html

2)exec格式:RUN [“可执行文件”, “参数1”, “参数2”]

如在新镜像中用yum方式安装nginx:

RUN ["yum","install","nginx"]
# 注:多行命令不要写多个RUN,原因是Dockerfile中每一个指令都会建立一层.多少个RUN就构建了多少层镜像,会造成镜像的臃肿、多层,不仅仅增加了构件部署的时间,还容易出错,RUN书写时的换行符是\

1.3、COPY:复制文件

COPY命令用于将宿主机器上的的文件复制到镜像内,如果目的位置不存在,Docker会自动创建。但宿主机器用要复制的目录必须是和Dockerfile文件统计目录下。

格式:

COPY [--chown=<user>:<group>] <源路径>... <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]

如把宿主机中的package.json文件复制到容器中/usr/src/app/目录下:

COPY package.json /usr/src/app/

1.4、CMD:容器启动命令

CMD命令用于容器启动时需要执行的命令,CMD在Dockerfile中只能出现一次,如果出现多个,那么只有最后一个会有效。
其作用是在启动容器的时候提供一个默认的命令项。如果用户执行docker run的时候提供了命令项,就会覆盖掉这个命令,没提供就会使用构建时的命令。

格式:

shell 格式:CMD <命令>
exec 格式:CMD ["可执行文件", "参数1", "参数2"...]

如容器启动时进入bash(终端命令行):

CMD /bin/bash

也可以用exec写法:

CMD ["/bin/bash"]

1.5 MAINTAINER:指定作者

用来指定dockerfile的作者名称和邮箱,主要作用是为了标识软件的所有者是谁。
语法:

MAINTAINER <name> <email>

如:

MAINTAINER xiaoxuya xxxxx@qq.com
  • 1.6、EXPOSE:暴露端口

EXPOSE命名适用于设置容器对外映射的容器端口号,如tomcat容器内使用的端口8080,则用EXPOSE命令可以告诉外界该容器的8081端口对外,在构建镜像时
用docker run -p可以设置暴露的端口对宿主机器端口的映射。

语法:

EXPOSE <端口1> [<端口2>...]

如:

EXPOSE 8080

EXPOSE 8080 其实等价于 docker run -p 8080 当需要把8080端口映射到宿主机中的某个端口(如8888)以便外界访问时,则可以用docker run -p 8888:8080

  • 1.7、WORKDIR:配置工作目录

WORKDIR命令是为RUN、CMD、ENTRYPOINT指令配置工作目录其效果类似于Linux命名中的cd命令,用于目录的切换,但是和cd不一样的是:如果切换到的目录不存在,WORKDIR会为此创建目录。

语法:

WORKDIR path  # path 指定进入时的工作目录

如需要在nginx目录下创建一个hello.txt的文件:

# 进入/usr/local/nginx目录下
WORKDIR /usr/local/nginx# 进入/usr/local/nginx中的html目录下
WORKDIR html# 在html目录下创建了一个hello.txt文件
RUN echo 'hello' > hello.txt
  • 1.8、ENTRYPOINT:容器启动执行命名

ENTRYPOINT的作用和用法和CMD一模一样,但是ENTRYPOINT有和CMD有2处不一样:

  1. CMD的命令会被docker run的命令覆盖而ENTRYPOINT不会
  2. CMD和ENTRYPOINT都存在时,CMD的指令变成了ENTRYPOINT的参数,并且此CMD提供的参数会被 docker run 后面的命令覆盖
  • 1.9、VOLUME

VOLUME用来创建一个可以从本地主机或其他容器挂载的挂载点。例如我们知道tomcat的webapps目录是放web应用程序代码的地方,此时我们要把webapps目录挂载为匿名卷,这样任何写入webapps中的心都不会被记录到容器的存储层,让容器存储层无状态化。

格式:

VOLUME ["path"]

如创建tomcat的webapps目录的一个挂载点

VOLUME /usr/local/tomcat/webapps

这样,在运行容器时,也可以用过docker run -v来把匿名挂载点挂载都宿主机器上的某个目录,如

docker run -d -v /home/tomcat_webapps:/usr/local/tomcat/webapps
  • 1.10、 USER

USER命令用于指定当前执行的用户,需要注意的是这个用户必须是已经存在,否则无法指定。它的用法和WORKDIR有点像,切换用户。

  • 1.11、ADD

  • 作用和使用方法和COPY一模一样,在此不重复讲述。

  • .12、ONBUILD

ONBUILD用于配置当前所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。
意思就是:这个镜像创建后,如果其它镜像以这个镜像为基础,会先执行这个镜像的ONBUILD命令

  • 1.13、ENV:设置环境变量

ENV命名用于设置容器的环境变量,这些变量以”key=value”的形式存在,在容器内被脚本或者程序调用,容器运行的时候这个变量也会保留。

格式:

  • 1) 设置一个: ENV
  • 2) 设置多个:ENV = =…

如设置一个环境变量JAVA_HOME,接下来的命名就可以使用这个变量:

ENV JAVA_HOME /opt/jdk
ENV PATH $PATH:$JAVA_HOME/bin

在使用ENV设置环境变量时,有几点需要注意:

  • 1)具有传递性,也就是当前镜像被用作其它镜像的基础镜像时,新镜像会拥有当前这个基础镜像所有的环境变量
  • 2)ENV定义的环境变量,可以在dockerfile被后面的所有指令(CMD除外)中使用,但不能被docker run 的命令参数引用

如:

ENV tomcat_home_name tomcat_7
RUN mkdir $tomcat_home_name

3)除了ENV之外,docker run -e 也可以设置环境变量传入容器内。

如:

docker run -d tomcat -e "tomcat_home_name=tomcat_7"

这样我们进入容器内部用ENV可以看到tomcat_home_name这个环境变量。

2、Dockerfile的编写

我们先看一个例子

#在centos上安装nginx
FROM centos
#标明著作人的名称和邮箱
MAINTAINER xiaoxuya xxxx@qq.com
#测试一下网络环境
RUN ping www.baidu.com
#安装nginx必要的一些软件(-y 对全部question回答yes)
RUN yum -y install gcc make pcre-devel zlib-devel tar zlib
#把nginx安装包复制到/usr/src/目录下(注意:dockerfile会自动解压,且安装包和dockerfile必须在同一目录)
copy nginx-1.15.8.tar.gz /usr/src/
#切换到/usr/src/nginx-1.15.8编译并且安装nginx (RUN书写时的换行符是\)
RUN cd /usr/src/nginx-1.15.8 \&& mkdir /usr/local/nginx \&& ./configure --prefix=/usr/local/nginx && make && make install \&& ln -s /usr/local/nginx/sbin/nginx /usr/local/sbin/ \&& nginx
#删除安装nginx安装目录
RUN rm -rf /usr/src/nginx-nginx-1.15.8
#对外暴露80端口(nginx默认端口)
EXPOSE 80
#启动nginx
CMD ["nginx", "-g", "daemon off;"]

上面的注释已经讲的非常清楚,其实不难发现,上面的例子就是类似于在centos系统上安装一个nginx的的一个过程,因此编写Dockerfile构建镜像就和在Linux上安装软件的流程几乎是一模一样的。所以我们在编写Dockerfile来构建镜像时,可以先思考在Linux上安装该软件的流程,再用Dockerfile提供的指令转化到Dockerfile中即可。

3、用Dockerfile构建镜像

用Dockerfile的核心在于编写Dockerfile,但是编写完之后我们需要知道怎么使用Dockerfile来构建镜像,下面以构建nginx镜像为例来简要说明构建流程

  • 3.1 上传安装包

首先我们需要把要构建的软件安装包上传到服务器中,我们可以在服务器目录上创建一个专门的文件夹,如:/var/nginx_build,然后把从nginx官网下载的nginx-1.15.8.tar.gz安装包上传到这个目录里。

  • 3.2 编写Dockerfile

如何编写nginx的Dockerfile上面已经详细介绍,现在我们只需把编写好的Dockerfile上传到/var/nginx_build目录下,当然你也可以在服务器上直接编写Dockerfile,但是要记得一定保证Dockerfile文件和安装包在一个目录下。

  • 3.3 运行构建命令构建

docker build 命令用于使用 Dockerfile 创建镜像。
格式:

docker build [OPTIONS] PATH | URL | -

OPTIONS有很多指令,下面列举几个常用的:

  • –build-arg=[] :设置镜像创建时的变量;
  • -f :指定要使用的Dockerfile路径;
  • –force-rm :设置镜像过程中删除中间容器;
  • –rm :设置镜像成功后删除中间容器;
  • –tag, -t: 镜像的名字及标签,通常 name:tag 或者 name 格式;

因此我们构建nginx可以用以下命令:

当Dockerfile和当前执行命令的目录不在同一个时,我们也可以指定Dockerfile,如

docker build -f /var/nginx_build/Dockerfile .

注意:命令行末尾是有 “.” 的

执行命名之后,会看到控制台逐层输出构建内容,直到输出两个Successfully即为构建成功。

4、实战:使用dockerFile 构建tomcat镜像,并运行

1、准备tomcat,jdk

2、编写dockerfile

vim Dockerfile# 编写dockerfile
FROM centos
MAINTAINER xiaoxuya 1848734470@qq.comADD apache-tomcat-9.0.45.tar.gz /usr/local/
ADD jdk-8u261-linux-x64.tar.gz /usr/local/RUN yum -y install vimENV MYPATH /usr/local
WORKDIR $MYPATHENV JAVA_HOME /usr/local/jdk1.8.0_261
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.45
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/binEXPOSE 8080CMD /usr/local/apache-tomcat-9.0.45/bin/startup.sh && tail -F /url/local/apache-tomcat-9.0.45/bin/logs/catalina.out

3、通过dockerfile构建镜像

[root@VM-8-9-centos test]# docker build -f ./Dockerfile -t tomcat-image:1.0 .
Sending build context to Docker daemon  154.6MB
Step 1/12 : FROM centos---> 300e315adb2f
Step 2/12 : MAINTAINER xiaoxuya 1848734470@qq.com---> Running in d0140dde4349
Removing intermediate container d0140dde4349---> 898209e32eda
Step 3/12 : ADD apache-tomcat-9.0.45.tar.gz /usr/local/---> f31e888f965e
Step 4/12 : ADD jdk-8u261-linux-x64.tar.gz /usr/local/---> d29625c0deaa
Step 5/12 : RUN yum -y install vim---> Running in 29e01b84012a
CentOS Linux 8 - AppStream                      126 kB/s | 7.5 MB     01:01
CentOS Linux 8 - BaseOS                         2.8 MB/s | 2.6 MB     00:00
CentOS Linux 8 - Extras                          16 kB/s | 9.6 kB     00:00
Dependencies resolved.
================================================================================Package             Arch        Version                   Repository      Size
================================================================================
Installing:vim-enhanced        x86_64      2:8.0.1763-15.el8         appstream      1.4 M
Installing dependencies:gpm-libs            x86_64      1.20.7-17.el8             appstream       39 kvim-common          x86_64      2:8.0.1763-15.el8         appstream      6.3 Mvim-filesystem      noarch      2:8.0.1763-15.el8         appstream       48 kwhich               x86_64      2.21-12.el8               baseos          49 kTransaction Summary
================================================================================
Install  5 PackagesTotal download size: 7.8 M
Installed size: 30 M
Downloading Packages:
[MIRROR] gpm-libs-1.20.7-17.el8.x86_64.rpm: Curl error (28): Timeout was reached for http://mirrors.nju.edu.cn/centos/8.4.2105/AppStream/x86_64/os/Packages/gpm-libs-1.20.7-17.el8.x86_64.rpm [Connection timed out after 30000 milliseconds]
[MIRROR] vim-common-8.0.1763-15.el8.x86_64.rpm: Curl error (28): Timeout was reached for http://mirrors.nju.edu.cn/centos/8.4.2105/AppStream/x86_64/os/Packages/vim-common-8.0.1763-15.el8.x86_64.rpm [Connection timed out after 30000 milliseconds]
[MIRROR] vim-enhanced-8.0.1763-15.el8.x86_64.rpm: Curl error (28): Timeout was reached for http://mirrors.nju.edu.cn/centos/8.4.2105/AppStream/x86_64/os/Packages/vim-enhanced-8.0.1763-15.el8.x86_64.rpm [Connection timed out after 30000 milliseconds]
(1/5): gpm-libs-1.20.7-17.el8.x86_64.rpm        1.3 kB/s |  39 kB     00:30
(2/5): vim-filesystem-8.0.1763-15.el8.noarch.rp 537 kB/s |  48 kB     00:00
(3/5): vim-enhanced-8.0.1763-15.el8.x86_64.rpm   46 kB/s | 1.4 MB     00:30
(4/5): which-2.21-12.el8.x86_64.rpm             287 kB/s |  49 kB     00:00
(5/5): vim-common-8.0.1763-15.el8.x86_64.rpm    211 kB/s | 6.3 MB     00:30
--------------------------------------------------------------------------------
Total                                           253 kB/s | 7.8 MB     00:31
warning: /var/cache/dnf/appstream-02e86d1c976ab532/packages/gpm-libs-1.20.7-17.el8.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID 8483c65d: NOKEY
CentOS Linux 8 - AppStream                      1.6 MB/s | 1.6 kB     00:00
Importing GPG key 0x8483C65D:Userid     : "CentOS (CentOS Official Signing Key) <security@centos.org>"Fingerprint: 99DB 70FA E1D7 CE22 7FB6 4882 05B5 55B3 8483 C65DFrom       : /etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
Key imported successfully
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transactionPreparing        :                                                        1/1 Installing       : which-2.21-12.el8.x86_64                               1/5 Installing       : vim-filesystem-2:8.0.1763-15.el8.noarch                2/5 Installing       : vim-common-2:8.0.1763-15.el8.x86_64                    3/5 Installing       : gpm-libs-1.20.7-17.el8.x86_64                          4/5 Running scriptlet: gpm-libs-1.20.7-17.el8.x86_64                          4/5 Installing       : vim-enhanced-2:8.0.1763-15.el8.x86_64                  5/5 Running scriptlet: vim-enhanced-2:8.0.1763-15.el8.x86_64                  5/5 Running scriptlet: vim-common-2:8.0.1763-15.el8.x86_64                    5/5 Verifying        : gpm-libs-1.20.7-17.el8.x86_64                          1/5 Verifying        : vim-common-2:8.0.1763-15.el8.x86_64                    2/5 Verifying        : vim-enhanced-2:8.0.1763-15.el8.x86_64                  3/5 Verifying        : vim-filesystem-2:8.0.1763-15.el8.noarch                4/5 Verifying        : which-2.21-12.el8.x86_64                               5/5 Installed:gpm-libs-1.20.7-17.el8.x86_64         vim-common-2:8.0.1763-15.el8.x86_64    vim-enhanced-2:8.0.1763-15.el8.x86_64 vim-filesystem-2:8.0.1763-15.el8.noarchwhich-2.21-12.el8.x86_64             Complete!
Removing intermediate container 29e01b84012a---> cb913102b122
Step 6/12 : ENV MYPATH /usr/local---> Running in 8b0786ed778b
Removing intermediate container 8b0786ed778b---> 2975de7c5a12
Step 7/12 : WORKDIR $MYPATH---> Running in 0a9cc086492e
Removing intermediate container 0a9cc086492e---> a64196298976
Step 8/12 : ENV JAVA_HOME /usr/local/jdk1.8.0_261---> Running in 7c240082ebb5
Removing intermediate container 7c240082ebb5---> aa65632b8c09
Step 9/12 : ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.45---> Running in 73e762542984
Removing intermediate container 73e762542984---> 152b8206534c
Step 10/12 : ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/bin---> Running in ee92e18eff17
Removing intermediate container ee92e18eff17---> d0d0d24be807
Step 11/12 : EXPOSE 8080---> Running in 54efc89413b3
Removing intermediate container 54efc89413b3---> 3fdcefe0c0ea
Step 12/12 : CMD /usr/local/apache-tomcat-9.0.45/bin/startup.sh && tail -F /url/local/apache-tomcat-9.0.45/bin/logs/catalina.out---> Running in 3c57a982db89
Removing intermediate container 3c57a982db89---> 271418e6337f
Successfully built 271418e6337f
Successfully tagged tomcat-image:1.0

4、运行镜像

[root@VM-8-9-centos test]# docker run -d -p 9999:8080  --name tomcat-container -v /home/test/tomcat9.0/webapps:/usr/local/apache-tomcat-9.0.45/webapps -v /home/test/tomcat9.0/tomcatLogs:/usr/local/apache-tomcat-9.0.45/logs   tomcat-image:1.0

13、docker网络

1. Docker 网络理论

容器网络实质上是由 Dokcer 为应用程序所创造的虚拟环境的一部分,它能让应用从宿主机操作系统的网络环境中独立出来,形成容器自有的网络设备、IP 协议栈、端口套接字、IP 路由表、防火墙等等与网络相关的模块。

Docker 为实现容器网络,主要采用的架构由三部分组成:CNM、Libnetwork 和驱动。

1.1. CNM

  • Sandbox,提供了容器的虚拟网络栈,也即端口套接字、IP 路由表、防火墙、DNS 配置等内容。主要用于隔离容器网络与宿主机网络,形成了完全独立的容器网络环境。
  • **Network,Docker 内部的虚拟子网,**网络内的参与者相互可见并能够进行通讯。Docker 的虚拟网路和宿主机网络是存在隔离关系的,其目的主要是形成容器间的安全通讯环境。
  • Endpoint,就是虚拟网络的接口,就像普通网络接口一样,Endpoint 的主要职责是负责创建连接。在 CNM 中,终端负责将沙盒连接到网络。个人理解:Endpoint 与常见的网络适配器类似,也就意味着 Endpoint 只能接入某一个网络。因此,如果容器需要接入到多个网络,就需要多个 Endpoint。

我们将图中的三个容器从左到右依次标记为 1、2、3,那么容器 2 有两个 endpoint 并且分别接入 NetworkdA 和 NetworkB。那么容器 1 和容器 2 是可以实现通信的,因为都接入了 NetworkA。但是容器 3 和容器 1,以及容器 2 的两个 Endpoint 之间是不能通信的,除非有三层路由器的支持。

1.2. Libnetwork

Libnetwork 是 CNM 的标准实现。Libnetwork 是开源库,采用 Go 语言编写(跨平台的),也是 Docker 所使用的库,Docker 网络架构的核心代码都在这个库中。Libnetwork 实现了 CNM 中定义的全部三个组件,此外它还实现了本地服务发现、基于 Ingress 的容器负载均衡,以及网络控制层和管理层功能。

1.3. 驱动

如果说 Libnetwork 实现了控制层和管理层功能,那么驱动就负责实现数据层。比如网络的连通性和隔离性是由驱动来处理的。驱动通过实现特定网络类型的方式扩展了 Docker 网络栈,例如桥接网络和覆盖网络。

Docker 内置了若干驱动,通常被称作原生驱动或者本地驱动。比如 Bridge DriverHost DriverOverlay DriverMacLan DriverNone Driver 等等。第三方也可以编写 Docker 网络驱动,这些驱动被叫做远程驱动,例如 Calico、Contiv、Kuryr 以及 Weave 等。每个驱动负责创建其上所有网络资源的创建和管理。

其中 Bridge 和 Overlay 在开发过程中使用频率较高。

  • Bridge,Docker 容器的默认网络驱动,通过网桥来实现网络通讯。
  • Overlay,借助 Docker 集群模块 Docker Swarm 搭建的跨 Docker Daemon 网络。通过它可以搭建跨物理网络主机的虚拟网络,进而让不同物理机中运行的容器感知不到多个物理机的存在。

在 Docker 安装时,会自动安装一块 Docker 网卡称为 docker0,用于 Docker 各容器及宿主机的网络通信。

2. 桥接网络(docker0 docker网桥)

Docker 的 bridge 网络采用内置的 bridge 驱动,而 bridge 的底层采用的是 Linux 内核中 Linux bridge 技术(这意味着 bridge 是高性能并且是非常稳定的)。

那么 Linux 内核中 Linux bridge 应用于容器的话,到底是一个什么样的拓扑图呢?如图所示(这个拓扑关系不清楚接下去的很多东西难以理解,所以先贴出采用 bridge 之后的一个拓扑图),由于容器运行在自己单独的 network namespace 中,所以有单独的协议栈。容器中配置网关为 172.17.0.1,发出去的数据包先到达 br0,然后交给主机的协议栈,由于目的 IP 是外网 IP,且主机会开启 IP forward 功能,于是数据包通过主机的 eth0 发出去。由于 172.17.0.1 是内网 IP ,所以一般发出去之前会做 NAT 转换。由于要进过主机的协议栈并且要做 NAT 转换,所以性能上可能会差点,但是优点就是容器处于内网中,安全性相对要高点。

默认情况下,创建的容器在没有使用 --network 参数指定要加入的 docker 网络时,默认都是加入 Docker 默认的单机桥接网络,也就是下面的 name 为 bridge 的网络。(bridge 模式)

[root@VM-8-9-centos ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
686230481b0b   bridge    bridge    local
21ed29c01538   host      host      local
cc88e8f2cdd9   none      null      local

而默认的 bridge 网络是被映射到内核中为 docker0 的网桥上。

docker network inspect bridge

Docker 默认的 bridge 网络和 Linux 内核中的 “docker0” 网桥是一个对应关系,如图所示。bridge 是 Docker 中对网络的命名,而 docker0 是内核中网桥的名字。(个人理解:你就可以把 bridge 和 docker0 当成 Linux 网桥的两个名字,两个都是代表同一个东西。docker 为了管理网络,又给 docker0 这个网桥取名为 bridge)。

bridge是docker 的网络模式,docker0是docker安装时,虚拟化出来的网桥

那么,容器在没有指定要加入的网络情况下,都是加入这个网络的,假如之后的拓扑图跟前面的一样。另外,单机桥接网络中的容器想要对外发布服务的话,需要依赖于端口映射,这也是为啥我们在启动容器的时候需要指定端口映射关系 的原因。

下面我们通过创建一个新的 Docker 桥接网络来阐述容器内部的通信、端口映射等情况。

[root@VM-8-9-centos ~]# docker network create -d bridge localnet  # 创建一个新的网络
a516a89475f6d264a0771228794337834421869b686b633aea687a9159f468a8
[root@VM-8-9-centos ~]# docker network ls    查看docker的网络信息
NETWORK ID     NAME       DRIVER    SCOPE
686230481b0b   bridge     bridge    local
21ed29c01538   host       host      local
f55943e20201   localnet   bridge    local
cc88e8f2cdd9   none       null      local

通过 ifconfig 可以看到 一一个新的网络/网桥 docker0 为docker创建时,产生的网桥,docker0网桥目前没有任何容器连接

$ brctl show
bridge name     bridge id               STP enabled     interfaces
br-f55943e20201 8000.02421d9aa3e1       no
docker0         8000.0242be6b61dc       no$ ifconfig
br-f55943e20201 Link encap:Ethernet  HWaddr 02:42:1d:9a:a3:e1inet addr:172.18.0.1  Bcast:172.18.255.255  Mask:255.255.0.0......
docker0   Link encap:Ethernet  HWaddr 02:42:be:6b:61:dcinet addr:172.17.0.1  Bcast:172.17.255.255  Mask:255.255.0.0

2.2. 同个网络中的容器间通信

使用下面这条命令即可运行一个新的容器,并且让这个新容器加入到 localnet 这个网络中的。

# docker run -d -p 9999:8080 --name demo1 --network localnet tomcat--network 指定新运行的容器加入指定的网桥
注意:如果没有指定 --network  默认接入docker0网桥

我们查看网桥的情况,demo1 的网络接口连接到了网桥 br-f55943e20201 上,如图所示。

# brctl show
bridge name     bridge id               STP enabled     interfaces
br-f55943e20201 8000.02421d9aa3e1       no              vethf6a3fba
docker0         8000.0242be6b61dc       no

此时,一个新的容器,已经正式接入了网桥(也即我们通过docker network create创建的这个网络),并且分配给了容器一个ip,通过vethpart 实现互联

如果在相同的网络中继续接入新的容器,那么新接入的容器是可以通过 demo1 这个名称来 ping 通的。如下所示,我们创建了一个新的容器(demo2),并且在这个容器中直接 ping demo1 发现可以的 ping 通的。这是因为,demo2 运行了一个本地 DNS 解析器,该解析器会将该请求转发到 Docker 内部 DNS 服务器中。DNS 服务器中记录了容器启动时通过 --name 或者 --net-alias 参数指定的名称和容器之间的和映射关系。

为什么,可以ping通,因为处于同一网段

2.3. 暴露端口

同一个网络中的容器之间虽然可以互相 ping 通,但是并不意味着可以任意访问容器中的任何服务。Docker 为容器增加了一套安全机制,只有容器自身允许的端口,才能被其他容器所访问。如下所示,我们可以通过 docker ps 命令可以看到容器暴露给其他容器访问的端口是 80,那么我们只能容器的 80 端口进行访问,而不能对没有开放的 22 端口进行访问。

2.3.1 telnet安装

yum list | grep telnet*yum -y install telnet-server.x86_64
yum -y install telnet.x86_64

2.3.2 启动容器,默认接入docker0网桥

docker run -d  --name nginx01 nginx  # 默认80端口

2.3.3 查看网络情况,和网桥

[root@VM-8-9-centos ~]# docker network ls
NETWORK ID     NAME       DRIVER    SCOPE
686230481b0b   bridge     bridge    local
21ed29c01538   host       host      local
a516a89475f6   localnet   bridge    local
cc88e8f2cdd9   none       null      local
[root@VM-8-9-centos ~]# brctl show
bridge name bridge id       STP enabled interfaces
br-a516a89475f6     8000.02420cf9fa2e   no
docker0     8000.0242f503119d   no      veth9e0765d

可以看到docker0网桥,已经被连接

2.3.4 查看容器分配的ip地址 docker inspect container

docker inspect 9c6e0ff1111a"Networks": {"bridge": {"IPAMConfig": null,"Links": null,"Aliases": null,"NetworkID": "686230481b0b69d79e62859cc2fae73635384782595b694e449de0be492c42a9","EndpointID": "e5b31ab99546b6f27ebdd1b1a93e2ae7c6f8dbec972a86fe09bd301fc4f2fa72","Gateway": "172.17.0.1","IPAddress": "172.17.0.2","IPPrefixLen": 16,"IPv6Gateway": "","GlobalIPv6Address": "","GlobalIPv6PrefixLen": 0,"MacAddress": "02:42:ac:11:00:02","DriverOpts": null}}

2.3.5 测试

[root@VM-8-9-centos ~]# telnet 172.17.0.2 80
Trying 172.17.0.2...
Connected to 172.17.0.2.
Escape character is '^]'.
^CConnection closed by foreign host.
[root@VM-8-9-centos ~]# telnet 172.17.0.2 20
Trying 172.17.0.2...
telnet: connect to address 172.17.0.2: Connection refused

我们可以在镜像创建的时候定义要暴露的端口,也可以在容器创建时定义要暴露的端口,使用 --expose。如下所示,就额外暴露了 20、22 这两个端口。

$ docker container run -d --name web --expose 22 --expose 20 nginx$ docker container ls
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
4749dac32711        nginx               "/docker-entrypoint.…"   12 seconds ago      Up 10 seconds       20/tcp, 22/tcp, 80/tcp   web

容器的端口暴露类似于打开了容器的防火墙,具体能不能通过这个端口访问容器中的服务,还得看容器中有无应用监听并处理来自这个端口的请求。

2.4. 端口映射

上面提到的桥接网络中的容器只能与位于相同网络中的容器进行通信,假如一个容器想对外提供服务的话,需要进行端口映射。端口映射将容器的某个端口映射到 Docker 主机端口上。那么任何发送到该端口的流量,都会被转发到容器中。如图所示,容器内部开放端口为 80,该端口被映射到了 Docker 主机的 10.0.0.15 的 5000 端口上。最终访问 10.0.0.15:5000 的所有流量都会被转发到容器的 80 端口。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UzK6xcZY-1624331956452)(C:\Users\Meet\AppData\Roaming\Typora\typora-user-images\image-20210621163154564.png)]

对应命令

 docker run -d --name web --network localnet -p 5000:80 nginx

2.5. 容器没有ping命令

1、进入容器

docker exec -it container /bin/bash

2、下载ping

apt-get update && apt-get install iputils-ping

2.5. 不同网络中的容器互通(docker network connect)

1、环境准备

准备运行四个容器,两两位于不同的网络

[root@VM-8-9-centos ~]# docker run -d  -P --network localnet --name nginx01-localnet nginx
5ed00194d587e6ba733147ac0bd7e081207c05762d749652424224b64c83f8d8
[root@VM-8-9-centos ~]# docker run -d  -P --network localnet --name nginx02-localnet nginx
329d4c8c010d5983f8f47adf16a407f52f51f54f25a73b757bbe5135595cb7e3
[root@VM-8-9-centos ~]# docker run -d -P --name nginx01-docker0 nginx
00f4d93b39346b3405629b8a68b534ae83a786cea2ec49f670503f3048dcb8f3
[root@VM-8-9-centos ~]# docker run -d -P --name nginx02-docker0 nginx
d3ed4313286d0887a6d9317c660bbab3956bf4b21237e1985ff99fd86d74b195
[root@VM-8-9-centos ~]# docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS                                     NAMES
d3ed4313286d   nginx     "/docker-entrypoint.…"   6 seconds ago    Up 5 seconds    0.0.0.0:49157->80/tcp, :::49157->80/tcp   nginx02-docker0
00f4d93b3934   nginx     "/docker-entrypoint.…"   13 seconds ago   Up 12 seconds   0.0.0.0:49156->80/tcp, :::49156->80/tcp   nginx01-docker0
329d4c8c010d   nginx     "/docker-entrypoint.…"   43 seconds ago   Up 42 seconds   0.0.0.0:49155->80/tcp, :::49155->80/tcp   nginx02-localnet
5ed00194d587   nginx     "/docker-entrypoint.…"   51 seconds ago   Up 50 seconds   0.0.0.0:49154->80/tcp, :::49154->80/tcp   nginx01-localnet

查看网卡信息

[root@VM-8-9-centos ~]# brctl show
bridge name bridge id       STP enabled interfaces
br-a516a89475f6     8000.02420cf9fa2e   no      veth8e5bac5vethcdb74fa
docker0     8000.0242f503119d   no      vethc5f6fd1vethef2810b
2、测试(docker exec -it nginx01-docker0 ping nginx01-localnet)
[root@VM-8-9-centos ~]# docker network connect localnet nginx01-docker0
[root@VM-8-9-centos ~]# docker exec -it nginx01-docker0 ping nginx01-localnet
PING nginx01-localnet (172.18.0.2) 56(84) bytes of data.
64 bytes from nginx01-localnet.localnet (172.18.0.2): icmp_seq=1 ttl=64 time=0.106 ms64 bytes from nginx01-localnet.localnet (172.18.0.2): icmp_seq=2 ttl=64 time=0.049 ms

14、springBoot微服务打包成docker镜像

1、准备jar包

2、编写DockerFile

3、上传,build生成镜像

[root@VM-8-9-centos test]# ls
Dockerfile  test-0.0.1-SNAPSHOT.jar
[root@VM-8-9-centos test]# docker build -f Dockerfile  -t springboot-image
"docker build" requires exactly 1 argument.
See 'docker build --help'.Usage:  docker build [OPTIONS] PATH | URL | -Build an image from a Dockerfile
[root@VM-8-9-centos test]# docker build -f Dockerfile  -t springboot-image .
Sending build context to Docker daemon   17.6MB
Step 1/5 : FROM java:8
8: Pulling from library/java
5040bd298390: Pull complete
fce5728aad85: Pull complete
76610ec20bf5: Pull complete
60170fec2151: Pull complete
e98f73de8f0d: Pull complete
11f7af24ed9c: Pull complete
49e2d6393f32: Pull complete
bb9cdec9c7f3: Pull complete
Digest: sha256:c1ff613e8ba25833d2e1940da0940c3824f03f802c449f3d1815a66b7f8c0e9d
Status: Downloaded newer image for java:8---> d23bdf5b1b1b
Step 2/5 : COPY *.jar /app.jar---> 3a53cad9ae39
Step 3/5 : CMD ["--server.port=8080"]---> Running in b53ca055dafd
Removing intermediate container b53ca055dafd---> 3aa1b8767fea
Step 4/5 : EXPOSE 8080---> Running in 20c50a75f15c
Removing intermediate container 20c50a75f15c---> b8e7072c5ae8
Step 5/5 : ENTRYPOINT ["java","-jar","/app.jar"]---> Running in 5edbe3e7a251
Removing intermediate container 5edbe3e7a251---> d99e96e3c5dd
Successfully built d99e96e3c5dd
Successfully tagged springboot-image:latest

4、运行容器,访问

[root@VM-8-9-centos test]# docker images
REPOSITORY         TAG                               IMAGE ID       CREATED              SIZE
springboot-image   latest                            d99e96e3c5dd   About a minute ago   661MB
tomcat-image       1.0                               271418e6337f   36 hours ago         638MB
tomcat             9.0.48-jdk8-adoptopenjdk-openj9   d716ed54947b   5 days ago           386MB
redis              latest                            fad0ee7e917a   2 weeks ago          105MB
nginx              latest                            d1a364dc548d   3 weeks ago          133MB
mysql              8.0.25                            c0cdc95609f1   5 weeks ago          556MB
centos             latest                            300e315adb2f   6 months ago         209MB
java               8                                 d23bdf5b1b1b   4 years ago          643MB
[root@VM-8-9-centos test]# docker run -d -p 9999:8080 --name springboot-test springboot-image

15、redis集群

1、拉取镜像

docker  pull  redis:5.0.5

2、创建Redis容器

创建三个 redis 容器:

  • redis-node1:6379
  • redis-node2:6380
  • redis-node3:6381
docker create --name redis-node1 -v /data/redis-data/node1:/data -p 6379:6379 redis:5.0.5 --cluster-enabled yes --cluster-config-file nodes-node-1.confdocker create --name redis-node2 -v /data/redis-data/node2:/data -p 6380:6379 redis:5.0.5 --cluster-enabled yes --cluster-config-file nodes-node-2.confdocker create --name redis-node3 -v /data/redis-data/node3:/data -p 6381:6379 redis:5.0.5 --cluster-enabled yes --cluster-config-file nodes-node-3.conf

3、启动并组建集群

启动容器

首先通过命令docker start来启动3个Redis容器:

执行完运行命令后检查一下容器的启动情况:

如果出现上图情况,Exited (1) 3 seconds ago,可以通过 docker logs 查看:

如上提示的是权限问题,我们尝试修改一下权限:

chmod -R  777 /data

启动成功后如下图所示:

组建集群

查看3个Redis在Docker中分配的ip结点信息:

执行「docker inspect redis-node1」得到 redis-node1 ip 信息为:172.17.0.4
执行「docker inspect redis-node2」得到 redis-node2 ip 信息为:172.17.0.3
执行「docker inspect redis-node3」得到 redis-node3 ip 信息为:172.17.0.2

拿到 ip 信息后(每个人的ip信息可能不一样),接下来进入某一个容器进行组建集群:

# 这里以进入 node1 为例
docker exec -it redis-node1 /bin/bash# 接着执行组建集群命令(请根据自己的ip信息进行拼接)
redis-cli --cluster create 172.17.0.2:6379  172.17.0.3:6379  172.17.0.4:6379 --cluster-replicas 0

ok,此时集群搭建完了,我们接下来测试一下。

测试集群

使用 redis-cli -c 命令连接到集群结点,然后 set 值,set 值之后会自动重定向到 0.2 ip地址,然后通过 get 获取一下,获取成功证明集群有效。

4、存在的问题

按照如上的步骤,虽然集群搭建成功了,但其实还是有点问题的,由于集群结点中的 ip地址 是docket内部分配的,如:172.17.0.2 等,如果使用 redis集群 的项目跟集群不在一台服务器上,那么项目是没法使用集群的,因为是访问不通的。

一种解决方案是让Docker使用 host模式 的网络连接类型,Docker在使用host模式下创建的容器是没有自己独立的网络命名空间的,是跟物理机共享一个网络空间,进而可以共享物理机的所有端口与IP,这样就可以让公共网络直接访问容器了,尽管这种方式有安全隐患,但目前来说还没找到其他可行性模式。

就存在的问题我们重新采用 host模式,重新创建一下容器:

1、停止已运行的容器
docker stop redis-node1 redis-node2 redis-node3
2、删除之前创建的容器
docker rm redis-node1 redis-node2 redis-node3# 清空上面创建的配置文件
rm -rf /data/redis-data/node*
3、重新基于host模式创建
docker create --name redis-node1 --net host -v /data/redis-data/node1:/data redis:5.0.5 --cluster-enabled yes --cluster-config-file nodes-node-1.conf --port 6379docker create --name redis-node2 --net host -v /data/redis-data/node2:/data redis:5.0.5 --cluster-enabled yes --cluster-config-file nodes-node-2.conf --port 6380docker create --name redis-node3 --net host -v /data/redis-data/node3:/data redis:5.0.5 --cluster-enabled yes --cluster-config-file nodes-node-3.conf --port 6381

跟之前创建命令不同,一是指定了 --net 网络类型为 host,二是这种情况下就不需要端口映射了,比如 -p 6379:6379,因为此时需要对外共享容器端口服务,所以只需要指定对外暴露的端口 -p 6379-p 6380 等。

4、启动容器并组建集群
# 启动命令
docker start redis-node1 redis-node2 redis-node3# 进入某一个容器
docker exec -it redis-node1 /bin/bash# 组建集群,10.211.55.4为当前物理机的ip地址
redis-cli --cluster create 10.211.55.4:6379  10.211.55.4:6380  10.211.55.4:6381 --cluster-replicas 0

5、查看集群信息
root@CentOS7:/data# redis-cli
127.0.0.1:6379> cluster nodes
72c291c32815194b64d1f6d0fdf771f5cc04e14a 10.211.55.4:6380@16380 master - 0 1590905997358 2 connected 5461-10922
6a595b67bbff15c94e5874c2d2cd556d6a6a6c17 10.211.55.4:6381@16381 master - 0 1590905998362 3 connected 10923-16383
4e3dbdc8f835dcbc38291c88f08165ee51d53d3d 10.211.55.4:6379@16379 myself,master - 0 1590905997000 1 connected 0-5460
127.0.0.1:6379>
6、测试集群

使用 redis-cli -c 连接到集群上,set一个值,然后从其他节点再获取值查看是否成功:

root@CentOS7:/data# redis-cli -c
127.0.0.1:6379> set wxiaowei 123
-> Redirected to slot [7515] located at 10.211.55.4:6380
OK
10.211.55.4:6380> get wxiaowei
"123"

未完待续

个人博客:https://www.xiaoxuya.top/

docker 使用理解 全流程相关推荐

  1. Docker常用命令-全流程

    Docker环境安装 安装详细说明参考官方文档:https://docs.docker.com/get-docker/ 环境信息查看 ##查看docker容器版本 docker version ##查 ...

  2. rabbitmq 使用与理解全流程

    1.什么是MQ MQ(message queue),从字面意思上看消息排队,本质是个队列,FIFO 先入先出,只不过队列中存放的内容是message ,MQ还是一种跨进程的通信机制,用于上下游传递消息 ...

  3. 在Docker 上完成对Springboot+Mysql+Redis的前后端分离项目的部署(全流程,全截图)

    本文章全部阅读大约2小时,包含一个完整的springboot + vue +mysql+redis前后端分离项目的部署在docker上的全流程,比较复杂,请做好心理准备,遇到问题可留言或则私信 目录 ...

  4. MMOCR: OpenMMLab 全流程的文字检测识别理解工具箱

    号外号外,继 港中文-商汤OpenMMLab开源全景图!之后,OpenMMLab 又有新成员加入咯-       01       MMOCR 特点 全流程:支持文字检测.文字识别以及其下游任务,比如 ...

  5. 从生产到理解分发,第三届“马栏山杯”算法大赛带你了解视频平台AI全流程...

    点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入! 从生产到理解分发 第三届"马栏山杯"算法大赛 带你了解视频平台AI全流程       -立足现有业务,朝元宇宙出发 ...

  6. 写一段代码提高内存占用_记录一次生产环境中Redis内存增长异常排查全流程!...

    点击上方 IT牧场 ,选择 置顶或者星标 技术干货每日送达 最近 DBA 反馈线上的一个 Redis 资源已经超过了预先设计时的容量,并且已经进行了两次扩容,内存增长还在持续中,希望业务方排查一下容量 ...

  7. 【Docker学习笔记 二】Docker安装、运行流程与常用命令

    上一篇Blog详细介绍了Docker为什么会出现,是为了解决什么问题而出现:Docker的基本组成部分.架构.本篇Blog就来详细了解下Docker如何安装.卸载以及常用的操作命令有哪些.因为Dock ...

  8. Blender 3.0机器人硬面建模材质渲染全流程学习课程

    学习在Blender中建模硬表面机器人角色 你会学到什么 Blender 3.0建模工具 Blender 3.0硬面人物造型 机器人角色的UV展开 如何在Blender中渲染 MP4 |视频:h264 ...

  9. Blender+Substance Painter全流程制作真实的机器人学习教程

    MP4 |视频:h264,1280×720 |音频:AAC,44.1 KHz,2 Ch 语言:英语+中英文字幕(根据原英文字幕机译更准确) |时长:44节课(10h 52m) |大小解压后:9.9 G ...

最新文章

  1. 树莓派 更新 时间 时区
  2. linux怎么远程windows桌面,Windows系统怎么远程登陆桌面Linux?
  3. hyperworks2019安装教程
  4. Git 提交规范-Java程序员收藏必备
  5. MySQL 字符集相关问题
  6. mariadb mysql 5.7_MariaDB 10.1 和 MySQL 5.7 在普通商用硬件上的表现
  7. 3d max用不同目标做关键帧_3D动画制作流程大解析
  8. 关于PHP你可能不知道的10件事
  9. 20100823工作记录
  10. JSP的3种方式实现radio ,checkBox,select的默认选择值
  11. 附上堆和栈的区别 (转贴)
  12. 快递100 快递公司编码-标准国际
  13. golang Windows下编译linux可执行文件
  14. 浅谈游戏《底特律:变人》
  15. 复制链接到safari浏览器打开,如何从Safari浏览器获取网址
  16. python中并集符号_python中列表之间求差集、交集、并集
  17. 哲理小故事---理想和现实
  18. 【数字图像处理】特征提取轮廓特征对黑色素瘤恶性/良性分类
  19. 递推-骨牌铺方格 II (2021-07-23)
  20. vue项目中引入阿里 iconfont 图标 动态渲染导航菜单图标

热门文章

  1. c语言程序教师节祝福,教师节想对老师说的话可以用这个小程序传达
  2. 教你怎样把在网上看到的视频下到电脑上
  3. html font设置字号,CSS font-size字体文字大小样式属性
  4. DEV-C++双人抢滩登陆1.1版推出了
  5. CSS中渐变色样式以及box-sizing属性
  6. 0基础和小Q学前端---css(2)特殊的选择器
  7. win10安装CCSv8,mcsdk,seedxds560v2,软仿
  8. java stringbuilder 清空问题
  9. 网络教育计算机统考-Outlook操作题
  10. Delphi XE 利用FastMM4检测内存泄漏的设置