docker运行笔记

2019年10月20日

清理docker

你以前可能执行过一些docker命令,给镜像或容器命过名,或者当前有容器正在运行,占用了某些端口。如果不清理,下文的某些命令可能会失败。下面的清理脚本是暴力清理,会删除所有东西。如果你知道怎么清理不要的东西,或者会自己解决冲突,恐怕你也不用看本篇初学者笔记了。

# Stop all containers
docker stop $(docker ps -qa)

# Remove all containers
docker rm $(docker ps -qa)

# Remove all images
docker rmi -f $(docker images -qa)

# Remove all volumes
docker volume rm $(docker volume ls -q)

# Remove all networks
docker network rm $(docker network ls -q)

代码

[1]

最基本示例

FROM nginx
COPY ./index.html /usr/share/nginx/html/index.html
./Dockerfile[2]

这个dockerfile意思说把宿主机当前目录的index.html复制到镜像/usr/share/nginx/html/index.html。为了成功构建,我们还需要准备一份index.html文件。

<h1>Hello, Docker!</h1>
./index.html
docker build -t nginx:v3 .

构建镜像,并命名为nginx:v3。

docker run --name web3 -d -p 80:80 nginx:v3

将镜像实例化为容器,把该容器命名为web3。后台运行。把容器的80端口映射到宿主机的80端口。

这样打开http://localhost/就能看到

http://localhost/显示Hello, Docker!

这时运行docker ps,可以看到容器web3正在运行。

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
d3e2ade58356        nginx:v3            "nginx -g 'daemon of…"   14 seconds ago      Up 13 seconds       0.0.0.0:80->80/tcp   web3

探索与修改

运行docker run --name web3 -d -p 81:80 nginx:v3,试试看能不能把容器的80端口映射到宿主机的81端口。

docker: Error response from daemon: Conflict. The container name "/web3" is already in use by container "4815697d6ec798d533fb52c2e56cf185f93804af8662045ca87d267c6b37ccb1". You have to remove (or rename) that container to be able to reuse that name.
See 'docker run --help'.

出错了。原来,已经有一个容器叫做web3了,我们不能再实例化一个叫做web3的容器。于是,把--name选项删掉,命令成功执行。这时打开http://localhost:81能看到大字“Hello, Docker!”。

现在我们把dockerfile修改成

<h1>Hello, world!</h1>
./index.html

刷新http://localhost:81,发现文字没有改变!回想起来,dockerfile说的是把index.html复制到镜像/usr/share/nginx/html/index.html。现在镜像已经构建完毕,里面的index.html还是老的index.html。我们需要重新构建镜像。

docker build -t nginx:v3 .
docker run -d -p 80:80 nginx:v3

第二个命令失败,“docker: Error response from daemon: driver failed programming external connectivity on endpoint relaxed_burnell (cedd92a7cf630d7ba7fd28eea42fa2f5663bc14965aa5a9b5427d067d7b76075): Bind for 0.0.0.0:80 failed: port is already allocated.” 80端口被刚才的容器占用了,我们要停止那个容器。于是运行

docker container stop $(docker container ls -aq)
代码

这句话的意思是停止当前所有容器,也是相当暴力。然后重新运行docker run -d -p 80:80 nginx:v3。刷新http://localhost:80,发现终于正确显示“Hello, world!”了。

优化

上面的步骤有一个不好,就是我们要指定镜像名称。我们平时练习,不想给那些乱七八糟的镜像取名字,能不能不取名字直接编译、实例化镜像?

docker run --rm -p 80:80  $(docker build -q .)
代码[3]

docker build -q .意思是构建镜像,且只输出镜像ID。外面的命令和刚才的差不多,删除了-d后台运行,添加了--rm,表示用户按下ctrl+c结束容器后,就把容器删除。

随便修改index.html,然后运行代码,你的修改应当立即生效。对dockerfile进行不合法的修改,运行上述命令,控制台应提示构建失败。

代码还有一个特点,就是会输出日志。每访问一次http://localhost:80控制台就会打印一些东西。如

$ docker run --rm -p 80:80  $(docker build -q .)
172.17.0.1 - - [20/Oct/2019:23:43:27 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36" "-"
172.17.0.1 - - [20/Oct/2019:23:43:28 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36" "-"
172.17.0.1 - - [20/Oct/2019:23:43:28 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36" "-"

因为当容器以前台模式运行时,docker会重定向stdout和stderr。[4]那具体是哪个进程在输出这些消息呢?已知我们的dockerfile没有运行任何进程,我们就要去其基镜像里看看了。

$ docker history --no-trunc --format "{{.ID}}: {{.CreatedBy}}"  nginx
sha256:5a9061639d0aeca4b13f8e18b985eea79e55168969d069febdb6723993ebba7d: /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon off;"]
<missing>: /bin/sh -c #(nop)  STOPSIGNAL SIGTERM
<missing>: /bin/sh -c #(nop)  EXPOSE 80
<missing>: /bin/sh -c ln -sf /dev/stdout /var/log/nginx/access.log     && ln -sf /dev/stderr /var/log/nginx/error.log
<missing>: /bin/sh -c set -x     && addgroup --system --gid 101 nginx     && adduser --system --disabled-login --ingroup nginx --no-create-home --home /nonexistent --gecos "nginx user" --shell /bin/false --uid 101 nginx     && apt-get update     && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 ca-certificates     &&     NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62;     found='';     for server in         ha.pool.sks-keyservers.net         hkp://keyserver.ubuntu.com:80         hkp://p80.pool.sks-keyservers.net:80         pgp.mit.edu     ; do         echo "Fetching GPG key $NGINX_GPGKEY from $server";         apt-key adv --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break;     done;     test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1;     apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/*     && dpkgArch="$(dpkg --print-architecture)"     && nginxPackages="         nginx=${NGINX_VERSION}-${PKG_RELEASE}         nginx-module-xslt=${NGINX_VERSION}-${PKG_RELEASE}         nginx-module-geoip=${NGINX_VERSION}-${PKG_RELEASE}         nginx-module-image-filter=${NGINX_VERSION}-${PKG_RELEASE}         nginx-module-njs=${NGINX_VERSION}.${NJS_VERSION}-${PKG_RELEASE}     "     && case "$dpkgArch" in         amd64|i386)             echo "deb https://nginx.org/packages/mainline/debian/ buster nginx" >> /etc/apt/sources.list.d/nginx.list             && apt-get update             ;;         *)             echo "deb-src https://nginx.org/packages/mainline/debian/ buster nginx" >> /etc/apt/sources.list.d/nginx.list                         && tempDir="$(mktemp -d)"             && chmod 777 "$tempDir"                         && savedAptMark="$(apt-mark showmanual)"                         && apt-get update             && apt-get build-dep -y $nginxPackages             && (                 cd "$tempDir"                 && DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)"                     apt-get source --compile $nginxPackages             )                         && apt-mark showmanual | xargs apt-mark auto > /dev/null             && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; }                         && ls -lAFh "$tempDir"             && ( cd "$tempDir" && dpkg-scanpackages . > Packages )             && grep '^Package: ' "$tempDir/Packages"             && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list             && apt-get -o Acquire::GzipIndexes=false update             ;;     esac         && apt-get install --no-install-recommends --no-install-suggests -y                         $nginxPackages                         gettext-base     && apt-get remove --purge --auto-remove -y ca-certificates && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list         && if [ -n "$tempDir" ]; then         apt-get purge -y --auto-remove         && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list;     fi
<missing>: /bin/sh -c #(nop)  ENV PKG_RELEASE=1~buster
<missing>: /bin/sh -c #(nop)  ENV NJS_VERSION=0.3.5
<missing>: /bin/sh -c #(nop)  ENV NGINX_VERSION=1.17.4
<missing>: /bin/sh -c #(nop)  LABEL maintainer=NGINX Docker Maintainers <docker-maint@nginx.com>
<missing>: /bin/sh -c #(nop)  CMD ["bash"]
<missing>: /bin/sh -c #(nop) ADD file:74b2987cacab5a6b067ccf3785408687d0bff53dbff198c6d8f06bed5187292c in / 

原来基镜像运行了nginx -g daemon offdocker run输出的日志就是从这里来的。如果nginx以后台方式运行,docker不光不会输出日志,而且容器会立即退出。[5]

总结

本文介绍了清理docker的代码、停止所有容器的代码、方便调试容器的一次性方法(代码)。

参考资料

. 利用 commit 理解镜像构成. . [2019-10-20].

参考资料

  1. beeman. remove-all-from-docker.sh. . 2016-11-15 [2019-10-20].
  2. 不少文章说Dockerfile的首字母必须大写,但我在Ubuntu上运行docker 19.03,发现小写的dockerfile也能被docker自动加载。为了以后能及时发现错误,下文始终用小写的dockerfile。
  3. starthal. Build and run Dockerfile with one command. . 2018-07-12 [2019-10-20].
  4. . Docker run reference. . [2019-10-20].
  5. . CMD 容器启动命令. . [2019-10-20].