Process Context ------------------------------------------- One of the most important parts of a process is the executing program code. This code is read in from an executable file and executed within the program's address space. Normal program execution occurs in user-space. When a program executes a system call or triggers an exception, it enters kernel-space. At this point, the kernel is said to be "executing on behalf of the process" and is in process context. When in process context, the current macro is valid[7]. Upon exiting the kernel, the process resumes execution in user-space, unless a higher-priority process has become runnable in the interim(过渡期), in which case the scheduler is invoked to select the higher priority process.
Other than process context there is interrupt context, In interrupt context, the system is not running on behalf of a process, but is executing an interrupt handler. There is no process tied to interrupt handlers and consequently no process context.
System calls and exception handlers are well-defined interfaces into the kernel. A process can begin executing in kernel-space only through one of these interfaces -- all access to the kernel is through these interfaces.
-------------------------------------------
Interrupt Context------------------------------------------- When executing an interrupt handler or bottom half, the kernel is in interrupt context. Recall that process context is the mode of operation the kernel is in while it is executing on behalf of a process -- for example, executing a system call or running a kernel thread. In process context, the current macro points to the associated task. Furthermore, because a process is coupled to the kernel in process context(因为进程是以进程上文的形式连接到内核中的), process context can sleep or otherwise invoke the scheduler.
Interrupt context, on the other hand, is not associated with a process. The current macro is not relevant (although it points to the interrupted process). Without a backing process(由于没有进程的背景),interrupt context cannot sleep -- how would it ever reschedule?(否则怎么再对它重新调度?) Therefore, you cannot call certain functions from interrupt context. If a function sleeps, you cannot use it from your interrupt handler -- this limits the functions that one can call from an interrupt handler.(这是对什么样的函数可以在中断处理程序中使用的限制)
Interrupt context is time critical because the interrupt handler interrupts other code. Code should be quick and simple. Busy looping is discouraged. This is a very important point; always keep in mind that your interrupt handler has interrupted other code (possibly even another interrupt handler on a different line!).Because of this asynchronous nature, it is imperative(必须) that all interrupt handlers be as quick and as simple as possible. As much as possible, work should be pushed out from the interrupt handler and performed in a bottom half, which runs at a more convenient time.
The setup of an interrupt handler's stacks is a configuration option. Historically, interrupt handlers did not receive(拥有) their own stacks. Instead, they would share the stack of the process that they interrupted[1]. The kernel stack is two pages in size; typically, that is 8KB on 32-bit architectures and 16KB on 64-bit architectures. Because in this setup interrupt handlers share the stack, they must be exceptionally frugal(必须非常节省) with what data they allocate there. Of course, the kernel stack is limited to begin with, so all kernel code should be cautious.
A process is always running. When nothing else is schedulable, the idle task runs.
Now I’m going to create a Dockerfile, that will build an image from the Alpine base image and in this image I’m writing three random files that each consist of 1 block with block size 1GB, by utilizing the dd command, taking up a total of 3 gigabytes. Since I’m not planning to do anything usefull with this image, the CMD simply defaults to /bin/true.
FROM alpine
RUN dd if=/dev/zero of=1g1.img bs=1G count=1
RUN dd if=/dev/zero of=1g2.img bs=1G count=1
RUN dd if=/dev/zero of=1g3.img bs=1G count=1
CMD /bin/true
Under the same system namespace Docker provides another command docker system prune, that will help you clean up dangling images, unused containers, stale volumes and networks.
After this commands greets us with a warning, it will remove all the stopped containers and dangling images. In our case, the intermediary image, the one with the 3 x 1GB files isn’t associated anymore, hence ‘dangling’, so it is pruned. Also all the intermediary images that were created while I was building my image get removed, and the total reclaimed space is about three gigabytes. Win!
$ docker system prune -a
By running this you’re basically cleaning up your entire system and only keep the things that are actually running on you r system, so be very aware of what you are doing when running this. For instance, you don’t want to run this prune -a command on a production server where you have some sidecar images idling (eg. scheduled backup or rollup, weekly exports, etc.), waiting to be executed every once in a while, because those will be cleaned up and need to be pulled in again when running your sidecar.
As of Docker 1.2, restart policies are the built-in Docker mechanism for restarting containers when they exit. If set, restart policies will be used when the Docker daemon starts up, as typically happens after a system boot. Restart policies will ensure that linked containers are started in the correct order.
The docker stop command attempts to stop a running container first by sending a SIGTERM signal to the root process (PID 1) in the container. If the process hasn't exited within the timeout period a SIGKILL signal will be sent.
RUN executes command(s) in a new layer and creates a new image. E.g., it is often used for installing software packages.
CMD sets default command and/or parameters, which can be overwritten from command line when docker container runs.
ENTRYPOINT configures a container that will run as an executable.
ENV name John Dow
ENTRYPOINT echo "Hello, $name"
when container runs as docker run -it <image> will produce output
Hello, John Dow
When instruction is executed in exec form it calls executable directly, and shell processing does not happen. For example, the following snippet in Dockerfile
ENV name John Dow
ENTRYPOINT ["/bin/echo", "Hello, $name"]
when container runs as docker run -it <image> will produce output
Hello, $name
Note that variable name is not substituted.
ENV name John Dow
ENTRYPOINT ["/bin/bash", "-c", "echo Hello, $name"]
CMD ["param1","param2"] (sets additional default parameters for ENTRYPOINT in exec form)
CMD command param1 param2 (shell form)
https://www.ctl.io/developers/blog/post/dockerfile-entrypoint-vs-cmd/ use ENTRYPOINT or CMD to specify your image's default executable both ENTRYPOINT and CMD give you a way to identify which executable should be run when a container is started from your image. In fact, if you want your image to be runnable (without additional docker run command line arguments) you must specify an ENTRYPOINT or CMD.
Trying to run an image which doesn't have an ENTRYPOINT or CMD declared will result in an error
$ docker run alpine
FATA[0000] Error response from daemon: No command specified
Many of the Linux distro base images that you find on the Docker Hub will use a shell like /bin/sh or /bin/bash as the the CMD executable. This means that anyone who runs those images will get dropped into an interactive shell by default (assuming, of course, that they used the -i and -t flags with the docker run command).
FROM ubuntu:trusty
CMD ping localhost
we can override the default CMD by specifying an argument after the image name when starting the container:
$ docker run demo hostname
The default ENTRYPOINT can be similarly overridden but it requires the use of the --entrypoint flag:
$ docker run --entrypoint hostname demo
Given how much easier it is to override the CMD, the recommendation is use CMD in your Dockerfile when you want the user of your image to have the flexibility to run whichever executable they choose when starting the container
In contrast, ENTRYPOINT should be used in scenarios where you want the container to behave exclusively as if it were the executable it's wrapping. That is, when you don't want or expect the user to override the executable you've specified.
There are many situations where it may be convenient to use Docker as portable packaging for a specific executable. Imagine you have a utility implemented as a Python script you need to distribute but don't want to burden the end-user with installation of the correct interpreter version and dependencies. You could package everything in a Docker image with an ENTRYPOINT referencing your script. Now the user can simply docker run your image and it will behave as if they are running your script directly.
Of course you can achieve this same thing with CMD, but the use of ENTRYPOINT sends a strong message that this container is only intended to run this one command.
Both the ENTRYPOINT and CMD instructions support two different forms: the shell formand the exec form. In the example above, we used the shell form which looks like this:
CMD executable param1 param2
When using the shell form, the specified binary is executed with an invocation of the shell using /bin/sh -c. You can see this clearly if you run a container and then look at the docker ps output
but there are some subtle issues that can occur when using the shell form of either the ENTRYPOINT or CMD instruction. If we peek inside our running container and look at the running processes we will see something like this:
Note how the process running as PID 1 is not our ping command, but is the /bin/shexecutable. This can be problematic if we need to send any sort of POSIX signals to the container since /bin/sh won't forward signals to child processes
you may also run into problems with the shell form if you're building a minimal image which doesn't even include a shell binary. When Docker is constructing the command to be run it doesn't check to see if the shell is available inside the container -- if you don't have /bin/sh in your image, the container will simply fail to start.
A better option is to use the exec form of the ENTRYPOINT/CMD instructions which looks like this:
CMD ["executable","param1","param2"]
When the exec form of the CMD instruction is used the command will be executed without a shell.
Now /bin/ping is being run directly without the intervening shell process (and, as a result, will end up as PID 1 inside the container).
Whether you're using ENTRYPOINT or CMD (or both) the recommendation is to always use the exec form so that's it's obvious which command is running as PID 1 inside your container.
Combining ENTRYPOINT and CMD allows you to specify the default executable for your image while also providing default arguments to that executable which may be overridden by the user.
FROM ubuntu:trusty
ENTRYPOINT ["/bin/ping","-c","3"]
CMD ["localhost"]
When both an ENTRYPOINT and CMD are specified, the CMD string(s) will be appended to the ENTRYPOINT in order to generate the container's command string. Remember that the CMD value can be easily overridden by supplying one or more arguments to `docker run` after the name of the image.
docker run ping docker.io
When using ENTRYPOINT and CMD together it's important that you always use the exec form of both instructions. Trying to use the shell form, or mixing-and-matching the shelland exec forms will almost never give you the result you want.
build can be specified either as a string containing a path to the build context, or an object with the path specified under context and optionally dockerfile and args.
If you specify image as well as build, then Compose names the built image with the webapp and optional tag specified in image:
build: ./dir
image: webapp:tag
This will result in an image named webapp and tagged tag, built from ./dir.
CONTEXT
Either a path to a directory containing a Dockerfile, or a url to a git repository.
When the value supplied is a relative path, it is interpreted as relative to the location of the Compose file. This directory is also the build context that is sent to the Docker daemon.
ARGS
Add build arguments, which are environment variables accessible only during the build process.
You can omit the value when specifying a build argument, in which case its value at build time is the value in the environment where Compose is running.
args: - buildno
- password
ESTART_POLICY
Configures if and how to restart containers when they exit. Replaces restart.
condition: One of none, on-failure or any (default: any).
delay: How long to wait between restart attempts, specified as a duration (default: 0).
max_attempts: How many times to attempt to restart a container before giving up (default: never give up).
window: How long to wait before deciding if a restart has succeeded, specified as a duration (default: decide immediately).
Express dependency between services, which has two effects:
docker-compose up will start services in dependency order. In the following example, db and redis will be started before web.
docker-compose up SERVICE will automatically include SERVICE’s dependencies. In the following example, docker-compose up web will also create and start db and redis.
Configure a check that’s run to determine whether or not containers for this service are “healthy”. See the docs for theHEALTHCHECK Dockerfile instruction for details on how healthchecks work.
If images have depended children, forced removal is via the -f flag:
docker images -q | xargs docker rmi -f
ENV PATH="/opt/gtk/bin:$PATH"
When the docker build command is run with the --no-cache flag.
When a non-cacheable command such as apt-get update is given. All the following RUN instructions will be run again.
The CMD instruction provides the default command for a container to execute.
The WORKDIR instruction sets the working directory for the RUN, CMD, and ENTRYPOINT Dockerfile commands that follow it:
The VOLUME instruction will create a mount point with the given name and mark it as holding externally mounted volumes from the host or from other containers
RUN executes command(s) in a new layer and creates a new image. E.g., it is often used for installing software packages.
CMD sets default command and/or parameters, which can be overwritten from command line when docker container runs.
ENTRYPOINT configures a container that will run as an executable.
When Docker runs a container, it runs an image inside it. This image is usually built by executing Docker instructions, which add layers on top of existing image or OS distribution. OS distribution is the initial image and every added layer creates a new image.
https://github.com/docker/kitematic/issues/1010
When you run docker-machine env default it doesn't set the actual env variables. At the end of the message you will see something like:
# Run this command to configure your shell:
# eval "$(docker-machine env default)"
2. From your shell
docker-machine create --driver virtualbox default
docker-machine ls
So basically if one of your instruction in the Dockerfile fails to complete successfully, you will still have an image (that was created during the instruction before) that is usable.
This is really helpful for troubleshooting to figure out, why the instruction failed. ie: You can simply launch a container from the last image created during the build operation and debugg the failed instruction in Dockerfile by manually executing it.
docker images | grep nginx
docker run -d -p 80:80 --name my_nginx_test_container spillai/test_nginx_image nginx -g "daemon off;"
docker history 728d805bd6d0
cat Dockerfile
FROM ubuntu:14.04
MAINTAINER Sarath "sarath@slashroot.in"
RUN apt-get update
RUN apt-get install -y nginx
RUN echo 'Our first Docker image for Nginx' > /usr/share/nginx/html/index.html
CMD ["/usr/sbin/nginx", "-g", "daemon off;"]
EXPOSE 80
docker run -d -p 80:80 --name my_nginx_test_container spillai/test_nginx_image:1.0
https://coderwall.com/p/ewk0mq/stop-remove-all-docker-containers
One liner to stop / remove all of Docker containers:
docker stop $(docker ps -a -q)
docker rm $(docker ps -a -q)
docker logs - Shows us the standard output of a container.
docker commands
docker ps
docker run -t -i ubuntu:14.04 /bin/bash The -t flag assigns a pseudo-tty or terminal inside our new container and the -i flag allows us to make an interactive connection by grabbing the standard in (STDIN) of the container.
You can print the machine’s configuration by running the docker-machine env docker-vm command. This is how you can switch between machines.
# Use the docker-vm
eval "$(docker-machine env docker-vm)"
# Switch to the dev machine
eval "$(docker-machine env dev)"
# Get image from the hub
docker pull nimmis/apache-php5
# Create the container
docker run -tid nimmis/apache-php5
the last one (d) means that we want to run it in the background.
The -P option on the run command will automatically expose any ports needed from the container to the host machine, while the -p option lets you specify ports to expose from the container to the host.
# Automatically exposes the container ports to an available host port
docker run -tid -P nimmis/apache-php5
# We can also specify ports manually <host port>:<container port>
docker run -tid -p 80:80 nimmis/apache-php5
Container Volumes
Volumes are an easy way to share storage between your host machine and the container. They are initialized during the container’s creation and kept synced. In our case we want to mount /var/www to a local directory ~/Desktop/www/laravel_demo.
# Option syntax
docker run -v <local dir>:<container dir>
docker run -tid -p 80:80 -v ~/Desktop/www/laravel_demo:/var/www nimmis/apache-php5
You can log into the container using the exec command.
docker exec -it <container> bash
# Restart Apache
/etc/init.d/apache2 restart
Naming Containers
docker run -tid -p 80:80 -v ~/Desktop/www/laravel_demo:/var/www --name wazo_server nimmis/apache-php5
Docker Machines
A Docker machine is the VM that holds your images and containers
Docker Images
Docker images are OS boxes that contain some pre-installed and configured software. You can browse the list of available images on the Docker Hub. In fact, you can create your own image based on another one and push it to the Docker hub so that other users can use it.
Docker Containers
Docker containers are separate instances that we create from images.
docker-machine ip default
and I am able to browse my site from the host at http://192.168.59.103:49159/
docker run -d -p 8000:80 nginx
curl $(docker-machine ip dev):8000
docker-machine stop/start dev
use docker ps to find which port the container exposes
install Virtualbox which is a prerequisite to running docker on OSX:
brew cask install virtualbox
Install boot2docker
Boot2docker is a small script that helps download and setup a minimal Linux VM that will be in charge of running docker daemon.
brew install boot2docker
boot2docker init
boot2docker up
export DOCKER_HOST=tcp://localhost:4243
Install docker
brew install docker
docker version
http://blog.javabien.net/2014/03/17/upgrade-docker-and-boot2docker-on-osx/
First let’s upgrade docker and boot2docker:
$ brew update
$ brew upgrade docker
$ brew upgrade boot2docker
Now it’s very important to upgrade boot2docker’s image otherwise you’ll see this kind of message when you try to create new images:
Error: Multipart upload for build is no longer supported. Please upgrade your docker client.
See this issue
So let’s upgrade boot2docker’s image:
$ boot2docker stop
$ boot2docker delete
$ boot2docker download
$ boot2docker init
$ boot2docker up
Sometimes boot2docker won’t stop. I’ve had to manually shutdown the vm with VirtualBox’s GUI.
http://prismoskills.appspot.com/lessons/System_Design_and_Big_Data/Chapter_10_-_Docker.jsp
One of the most common scaling paradigm during the 2000s was that of creating several virtual machines, create design packs for installing standard softwares like Oracle, MySQL, ZooKeeper, Solr etc. and manage the lifecycle of these components through a browser. The "virtualization" technology at the core of this strategy creates several VMs (Virtual Machines) on a single powerful machine so that its resources are shared by each of these VMs. To scale horizontally then, one just keeps on adding more hardware and keeps on spinning more VMs on them.
The above solution works well till the number of VMs is in thousands.
When the count of VMs goes beyond that, the cost of hardware becomes really high and so is its maintenance.
Besides, the cost of spinning a VMs is high and can take several minutes.
So if a service is designed to be elastic such that it adds machines based on demand, then it could
take quite sometime to add those VMs during high demand and could cause the website to appear slow while
new VMs provisioning is in progress.
Google faced this problem in the 2005 and its engineers came out with Control Groups (also called as cgroups or Process Containers) in 2007. CGroups is a feature in Linux kernel that limits and isolates the resource usage of a group of processes. With such isolation, processes in one group cannot affect those in the other. And since a limit is enforced on each group, processes in a single group cannot consume all the resources of a given machine unless explicitly so configured.
Control Groups vs VMs
Running an isolated group of processes is much less intensive than running a full blown VM.
This becomes evident when you realize that unlike a VM, CGroups does not try to emulate the hardware layer for some OS running on them.
A VM provides a virtual hardware-like API layer to the OS that runs on it.
Since the VM does not know what will run on it, it has to provide all the APIs of the hardware layer.
This is wasteful since the application running on it may use only a handful of those APIs
The emulation of all these APIs adds time to the VM bring-up as well.
Eventually, a VM has to interact with the underlying hardware for accessing memory, network etc.
So, the emulation API has a redirection that passes these commands to the underlying hardware.
This redirection consumes some time, however little.
As seen in #1, since a VM is heavy, lot of hardware resources are wasted in trying to provide a common environment to all programs.
Imagine spinning a full-linux VM for an application whose only job is to do some number crunching or to deal with storage.
CGroups are free from the above problems and they can achieve better resource utilization by isolating their groups from each other. Hence CGroups are faster to bring up, faster for the processes that run in them (as the processes interact directly with the underlying hardware) and more groups can be run on a machine than VMs.
Downside of CGroups is that it runs only on Linux. So if you have to run them on Windows, you first need to install a VM running Linux and then install CGroups on the same.
Namespace Isolation
Namespace isolation is a feature in which groups of processes remain unaware of other groups' presence on the same machine. It was released in 2008.
Enter Docker
Docker makes use of CGroups and namespace isolation and allows creation of Docker images
A docker image is a collection of processes that are run in isolation by cgroups.
Roughly speaking, CGroups with Docker is equivalent to VirtualBox and Docker image is equivalent to the VM.
Just that Docker/CGroups does not provide a hardware-like API layer to any OS running on it.
It just makes sure that groups do not interfere with each other and stay within their allocated limits.
https://en.wikipedia.org/wiki/Cgroups#NAMESPACE-ISOLATION
cgroups (abbreviated from control groups) is a Linux kernel feature that limits, accounts for, and isolates the resource usage (CPU, memory, disk I/O, network, etc.) of a collection of processes.
One of the design goals of cgroups is to provide a unified interface to many different use cases, from controlling single processes (by using nice, for example) to whole operating system-level virtualization (as provided by OpenVZ, Linux-VServer or LXC, for example). Cgroups provides:
Resource limitation: groups can be set to not exceed a configured memory limit, which also includes the file system cache[6][7]
Prioritization: some groups may get a larger share of CPU utilization[8] or disk I/O throughput[9]
Accounting: measures how much resources certain systems use, which may be used, for example, for billing purposes[10]
Control: freezing the groups of processes, their checkpointing and restarting[10]
Use[edit]
Namespace isolation
While not technically part of the cgroups work, a related feature of the Linux kernel is namespace isolation, where groups of processes are separated such that they cannot "see" resources in other groups.
Docker Toolbox installs docker, docker-compose and docker-machine in /usr/local/bin on your Mac. It also installs VirtualBox. At installation time, Toolbox uses docker-machine to provision a VirtualBox VM called default, running theboot2docker Linux distribution, with Docker Engine with certificates located on your Mac at$HOME/.docker/machine/machines/default.
Before you use docker or docker-compose on your Mac, you typically use the command eval $(docker-machine env default) to set environment variables so that docker or docker-compose know how to talk to Docker Engine running on VirtualBox.
Docker for Mac does not use VirtualBox, but rather HyperKit, a lightweight OS X virtualization solution built on top of Hypervisor.framework in OS X 10.10 Yosemite and higher.
Installing Docker for Mac does not affect machines you created with Docker Machine. The install offers to copy containers and images from your local default machine (if one exists) to the new Docker for Mac HyperKit VM. If chosen, content from default is copied to the new Docker for Mac HyperKit VM, and your original defaultmachine is kept as is.
The Docker for Mac application does not use docker-machine to provision that VM; but rather creates and manages it directly.
The WORKDIR instruction sets the working directory for any RUN, CMD, ENTRYPOINT, COPY and ADD instructions that follow it in the Dockerfile. If the WORKDIR doesn’t exist, it will be created even if it’s not used in any subsequentDockerfile instruction.
Warning Avoid using your root directory, /, as the root of the source repository.
The docker build command will use whatever directory contains the Dockerfile
as the build context (including all of its subdirectories). The build context will be
sent to the Docker daemon before building the image, which means if you
use / as the source repository, the entire contents of your hard drive will get
sent to the daemon (and thus to the machine running the daemon). You
probably don't want that.
and
The <src> path must be inside the context of the build; you cannot
ADD ../something /something, because the first step of a docker build is
to send the context directory (and subdirectories) to the docker daemon.
Everytime docker successfully executes a RUN command from a Dockerfile, a new layer in the image filesystem is committed. Conveniently you can use those layers ids as images to start a new container.
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6934ada98de6 42e0228751b3 "/bin/sh -c './utils/" 24 minutes ago Exited (1) About a minute ago sleepy_bell
And then run the image [if necessary, running bash]:
$ docker run -it 7015687976a4 [bash -il]
Now you are actually looking at the state of the build at the time that it failed, instead of at the time before running the command that caused the failure.
docker-compose UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 0: ordinal not in range(128) Slack also changes --no-cache to –no-cache, which will result in the exact same error when copy pasted from there.
The "-t" flag adds a tag to the image so that it gets a nice repository name and tag. Also not the final ".", which tells Docker to use the Dockerfile in the current directory.
Running docker history will show you the effect of each command has on the overall size of the file:
I have been running Docker on OS X for quite a while now and the I switched to Docker Machine for Mac Beta few months back. It has been a great experience so far, but not without occasional hiccups. I run some of my containers in host networking mode, and I have faced the following problem when I start some of my containers.
java.net.UnknownHostException: moby: moby: Name or service not known
at java.net.InetAddress.getLocalHost(InetAddress.java:1505) ~[na:1.8.0_102]
at com.netflix.eureka.transport.JerseyReplicationClient.createReplicationClient(JerseyReplicationClient.java:170) ~[eureka-core-1.4.9.jar!/:1.4.9]
at com.netflix.eureka.cluster.PeerEurekaNodes.createPeerEurekaNode(PeerEurekaNodes.java:194) [eureka-core-1.4.9.jar!/:1.4.9]
....
Caused by: java.net.UnknownHostException: moby: Name or service not known
at java.net.Inet6AddressImpl.lookupAllHostAddr(Native Method) ~[na:1.8.0_102]
at java.net.InetAddress$2.lookupAllHostAddr(InetAddress.java:928) ~[na:1.8.0_102]
at java.net.InetAddress.getAddressesFromNameService(InetAddress.java:1323) ~[na:1.8.0_102]
at java.net.InetAddress.getLocalHost(InetAddress.java:1500) ~[na:1.8.0_102]
... 67 common frames omitted
‘Moby’ is the name given to the host system that runs behind the scenes with Docker Machine for Mac (and Windows). Docker Machine for Mac runs MobyLinux VM, and the hostname file refers to itself as ‘moby’. But this entry is missing in the /etc/hosts file and my services fail to start up because it cannot resolve ‘moby’ to an IP. The workaround is simple. Just add the ‘add-host’ flag to your Docker run command telling it that ‘moby’ is ‘127.0.0.1’, like so:
docker run --net=host --add-host=moby:127.0.0.1 yourid/yourimage
This will automatically add an entry to the /etc/hosts file saying that moby in fact is 127.0.0.1.
docker system prune命令可以用于清理磁盘,删除关闭的容器、无用的数据卷和网络,以及dangling镜像(即无tag的镜像)。docker system prune -a命令清理得更加彻底,可以将没有容器使用Docker镜像都删掉。注意,这两个命令会把你暂时关闭的容器,以及暂时没有用到的Docker镜像都删掉了…所以使用之前一定要想清楚吶。