一
CentOS7安装Docker
[root@docker ~]# yum install -y docker
[root@docker ~]# systemctl start docker
# 也可以使用官方安装脚本
curl -sSL https://get.docker.com/ | sh
- 运行镜像并打印hello world
[root@docker ~]# docker run busybox echo hello world
Unable to find image 'busybox:latest' locally
Trying to pull repository docker.io/library/busybox ...
latest: Pulling from docker.io/library/busybox
e2334dd9fee4: Pull complete
Digest: sha256:a8cf7ff6367c2afa2a90acd081b484cbded349a7076e7bdf37a05279f276bc12
Status: Downloaded newer image for docker.io/busybox:latest
hello world
# 容器一旦完成工作就会停止,使用-it参数可以获得一个交互式会话,但是退出终端,容器也会停止运行
- 以后台方式运行Docker容器
[root@docker ~]# docker run -d -p 1234:1234 python:2.7 python -m SimpleHTTPServer 1234
创建、启动、停止和移除容器
- create
- start
- stop:发送SIGTREM信号到容器,如果在一定时间内容器没有停止,则会再发送SIGKILL信号强制停止
- kill:发送SIGKILL信号到容器
- rm
一次性删除、停止所有容器
-
docker rm $(docker ps -a -q)
:-q参数只会返回容器的ID信息 -
docker rm -v $(docker ps -q)
:-v参数用来删除镜像中定义的数据卷 docker stop $(docker ps -a -q)
使用Dockerfile构建Docker镜像
# 创建一个名为Dockerfile的文件
[root@docker ~]# cat Dockerfile
FROM busybox
ENV foo=bar
# 通过docker build命令构建一个新镜像,名为busybox2
[root@docker ~]# docker build -t busybox2 .
Sending build context to Docker daemon 465.9 kB
Step 1/2 : FROM busybox
---> be5888e67be6
Step 2/2 : ENV foo bar
---> Running in b823072a4306
---> 42c79cbf4787
Removing intermediate container b823072a4306
Successfully built 42c79cbf4787
# 查看新构建的镜像
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
busybox2 latest 42c79cbf4787 22 seconds ago 1.22 MB
docker.io/busybox latest be5888e67be6 3 weeks ago 1.22 MB
单一容器中使用Supervisor运行WordPress
- 使用Supervisor来监控并运行MySQL和HTTPD.Supervisor不是一个init系统,二十一个用来控制多个进程的普通程序
两个链接在一起的容器运行WordPress
docker pull wordpress:latest
docker pull mysql:latest
# -e MYSQL_ROOT_PASSWORD:通过MYSQL_ROOT_PASSWORD环境变量来设置MySQL密码
docker run --name mysqlwp -e MYSQL_ROOT_PASSWORD=wordpressdocker -d mysql
docker run --name wordpress --link mysqlwp:mysql -p 80:80 wordpress
Docker设置MySQL环境变量
- 通过设置环境变量来创建数据库
docker run --name mysqlwp -e MYSQL_ROOT_PASSWORD=wordpressdocker -e MYSQL_DATABASE=wordpress -e MYSQL_USER=wordpress -e MYSQL_PASSWORD=wordpresspwd -d mysql
docker run --name wordpress --link mysqlwp:mysql -p 80:80 -e WORDPRESS_DB_NAME=wordpress -e WORDPRESS_DB_USER=wordpress -e WORDPRESS_DB_PASSWORD=wordpresspwd -d wordpress
备份在容器中的数据库
- 将宿主机的卷挂载到容器上
docker run --name mysqlwp -e MYSQL_ROOT_PASSWORD=wordpressdocker -e MYSQL_DATABASE=wordpress -e MYSQL_USER=wordpress -e MYSQL_PASSWORD=wordpresspwd -v /home/docker/mysql:/var/lib/mysql -d mysql
- 进行备份
docker exec mysqlwp mysqldump --all-databases --password=wordpressdocker > wordpress.backup
宿主机容器之间共享数据
- 通过-v选项将宿主机的卷挂载到容器中
- 默认情况下会以读写的模式挂载数据卷.如果只想以只读模式挂载,可以在卷名称后通过冒号设置相应的权限
docker run -it -v $pwd:/cookbook centos:7.6.1810 /bin/bash
docker run -it -v $pwd:/cookbook:ro centos:7.6.1810 /bin/bash
# 通过docker inspect查看数据卷的挂载映射情况
docker inspect -f {{.Mounts}} 997
[{bind /data /cookbook true rprivate}]
容器之间共享数据
- 通过-v选项省略宿主机的路径,就创建了一个称为数据容器的容器
docker run -it -v /cookbook centos:7.6.1810 /bin/bash
touch /share/test.txt
exit
# 在容器中创建的文件会存储在宿主机上
docker inspect -f {{.Mounts}} cbe
[{volume 9d4... /var/lib/docker/volumes/9d4.../_data /share local true }]
ls /var/lib/docker/volumes/9d446354a6118f5e6b55bbee92ff5e6ef8a2ce19da60a16b1ef4a5cbeb13601d/_data/
test.txt
- 将这个容器中的卷共享给其他容器,可以使用--volumes-from选项
docker run -v /data --name data centos:7.6.1810
# 这个容器没有处于运行状态,但是它的映射关系已经存在,并且被持久化到/var/lib/docker/volumes/下.可以通过docker rm -v data命令来删除容器和它的卷.
docker inspect -f {{.Mounts}} data
[{volume e5ebbc18722a2d43b81d373321aa183fd2452e9ae0f47c4f958e5fc8b3201468 /var/lib/docker/volumes/e5ebbc18722a2d43b81d373321aa183fd2452e9ae0f47c4f958e5fc8b3201468/_data /data local true }]
# 即使容器没有被运行,也可以通过--volumes-from来挂载其中的卷
[root@docker supervisor]# docker run -it --volumes-from data centos:7.6.1810 /bin/bash
[root@2cf48f4d03f7 /]# ls /data/
[root@2cf48f4d03f7 /]# touch /data/qqq.txt
# 在宿主机上查看
[root@docker supervisor]# docker inspect -f {{.Mounts}} 2cf
[{ e5ebbc18722a2d43b81d373321aa183fd2452e9ae0f47c4f958e5fc8b3201468 /var/lib/docker/volumes/e5ebbc18722a2d43b81d373321aa183fd2452e9ae0f47c4f958e5fc8b3201468/_data /data local true }]
[root@docker supervisor]# ls /var/lib/docker/volumes/e5ebbc18722a2d43b81d373321aa183fd2452e9ae0f47c4f958e5fc8b3201468/_data
qqq.txt
对容器数据进行复制
- 有一个运行的容器没有进行任何卷映射,但是要从容器内复制数据出来或者将数据复制到容器里
# 启动一个容器,并让它执行睡眠状态
# 将文件从容器复制到宿主机
[root@docker supervisor]# docker run -d --name testcopy centos:7.6.1810 sleep 360
22161f5fb220205c7b451684b1075c1e5949874c30611c2bfea726ac8b5fa966
[root@docker supervisor]# docker exec -it testcopy /bin/bash
[root@22161f5fb220 /]# cd /root
[root@22161f5fb220 ~]# ls
anaconda-ks.cfg
[root@22161f5fb220 ~]# echo "hello" > file.txt
[root@22161f5fb220 ~]# exit
[root@docker ~]# docker cp testcopy:/root/file.txt .
[root@docker ~]# ls
anaconda-ks.cfg file.txt supervisor
[root@docker ~]# cat file.txt
hello
# 将文件从宿主机复制到容器
[root@docker ~]# echo host > host.txt
[root@docker ~]# docker cp host.txt testcopy:/root
[root@docker ~]# docker exec -it testcopy /bin/bash
[root@22161f5fb220 /]# ls /root
anaconda-ks.cfg file.txt host.txt
[root@22161f5fb220 /]# cat /root/host.txt
host
# 将一个容器中的文件复制到另一个容器
# 可以通过将临时主机作为文件的中转站,执行两次docker cp来实现
[root@docker ~]# docker cp testcopy:/root/file.txt /opt
[root@docker ~]# docker cp /opt/file.txt wordpress:/root
[root@docker ~]# docker exec -it wordpress /bin/bash
root@815a3662b5a6:/var/www/html# ls /root/
.bashrc .profile file.txt
root@815a3662b5a6:/var/www/html# cat /root/file.txt
hello
创建和共享镜像
- 在容器内部进行一些改动,对改动进行保存并且创建一个新镜像
[root@docker ~]# docker run -it ubuntu:14.04 /bin/bash
root@fa5f6c21e261:/# apt-get update
[root@docker ~]# docker commit fa5 ubuntu:update
sha256:76252050fc5133bf8b45891fb6a63cb83e274655b04d5bf1dbf2f1e9357a8347
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu update 76252050fc51 13 seconds ago 210 MB
docker.io/ubuntu 14.04 6e4f1fe62ff1 4 months ago 197 MB
- 可以通过docker diff命令来查看容器中对镜像做出的修改
[root@docker ~]# docker diff fa5
C /root
A /root/.bash_history
C /run
D /run/secrets
# A表示文件或目录时新增加的
# C表示文件内容有修改
# D表示项目已经删除
将容器和镜像保存为tar文件进行共享
- 对于已有镜像,可以使用Docker命令行的save和load命令来创建一个压缩包.对于容器,可以使用import和export进行导入导出操作
# 容器
[root@docker ~]# docker export cbe > update.tar
[root@docker ~]# ls
update.tar
[root@docker ~]# docker import - update < update.tar
sha256:f825b0e2c16889837bab19e9a899c0b612cc1f92383f053a25395762964d3312
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
update latest f825b0e2c168 5 seconds ago 202 MB
# 镜像
[root@docker ~]# docker save -o update1.tar update
[root@docker ~]# ls
update1.tar update.tar
[root@docker ~]# docker rmi update
Untagged: update:latest
Deleted: sha256:f825b0e2c16889837bab19e9a899c0b612cc1f92383f053a25395762964d3312
Deleted: sha256:cfec12fe565a566d3dfdec679a709e337c20fb2a2eec598e1a968bbd1e5c4fc4
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu update 76252050fc51 7 minutes ago 210 MB
[root@docker ~]# docker load < update1.tar
cfec12fe565a: Loading layer [==================================================>] 210.2 MB/210.2 MB
Loaded image: update:latest
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
update latest f825b0e2c168 2 minutes ago 202 MB
ubuntu update 76252050fc51 7 minutes ago 210 MB
编写Dockerfile
# ENTRYPOINT指令设置了从该镜像创建的容器启动时需要执行的命令
[root@docker ~]# cat Dockerfile
FROM ubuntu:14.04
ENTRYPOINT ["/bin/echo"]
# 构建镜像
[root@docker ~]# docker build .
Sending build context to Docker daemon 420.8 MB
Step 1/2 : FROM ubuntu:14.04
---> 6e4f1fe62ff1
Step 2/2 : ENTRYPOINT /bin/echo
---> Running in 61ae0c8d7a05
---> baca18a408d5
Removing intermediate container 61ae0c8d7a05
Successfully built baca18a408d5
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> baca18a408d5 7 seconds ago 197 MB
# 基于新构建的镜像启动容器
[root@docker ~]# docker run bac Hi Docker!
Hi Docker!
# 也可以使用CMD指令.这个指令的优点是,在启动容器时,通过在docker run命令后面指定新的CMD参数来覆盖Dockerfile文件中设置的内容
[root@docker ~]# cat Dockerfile
FROM ubuntu:14.04
CMD ["/bin/echo","Hi Docker!"]
[root@docker ~]# docker build .
Sending build context to Docker daemon 420.8 MB
Step 1/2 : FROM ubuntu:14.04
---> 6e4f1fe62ff1
Step 2/2 : CMD /bin/echo Hi Docker!
---> Running in 3451081c9675
---> c22a69815ca9
Removing intermediate container 3451081c9675
Successfully built c22a69815ca9
[root@docker ~]# docker run c22
Hi Docker!
# 指定command,结果是Dockerfile中的CMD被覆盖
[root@docker ~]# docker run c22 /bin/date
Fri May 8 13:32:42 UTC 2020
- 注意:CMD可以通过docker run后面的参数来覆盖,而ENTRYPOINT只能通过docker run的--entrypoint参数来覆盖
- 指定仓库名和标签:
docker build -t cookbook:hello .
将Flask打包到镜像
cat hello.py
#!/usr/bin/env python
from flask import Flask
app = Flask(__name__)
@app.route('/hi')
def hello_world():
return "Hello World!"
if __name__ == '__main__':
app.run(host='0.0.0.0',port=5000)
cat Dockerfile
FROM ubuntu:14.04
RUN apt-get update
RUN apt-get install -y python
RUN apt-get install -y python-pip
RUN apt-get clean all
RUN pip install flask
ADD hello.py /tmp/hello.py
EXPOSE 5000
CMD ["python","/tmp/hello.py"]
- 构建镜像:
docker build -t flask .
- 以后台方式运行镜像:
docker run -d -P flask
,-P选项表示随机映射一个宿主机端口到Dockerfile中设置的端口 - 覆盖CMD指令:
docker run -it -P flask python3 hello.py
- 访问浏览器查看结果:10.0.0.180/hi
优化Dockerfile
- 使用多条RUN指令安装软件包会向镜像增加不必要的层数
- 更应该使用COPY而不是ADD,ADD用于更复杂的文件复制场景
FROM ubuntu:14.04
RUN apt-get update && apt-get install -y \
python
python-pip
RUN pip install flask
COPY hello.py /tmp/hello.py
EXPOSE 5000
CMD ["python","/tmp/hello.py"]
- 使用官方python镜像
FROM python:3.6
RUN pip install flask
COPY hello.py /tmp/hello.py
EXPOSE 5000
CMD ["python3","/tmp/hello.py"]
通过标签对镜像进行版本管理
通过标签对镜像和镜像的版本进行跟踪,而不是使用镜像ID
使用docker tag命令为镜像打标签,允许对已有的镜像进行重命名,或者为同一个镜像创建新的标签
[root@docker supervisor]# docker tag docker.io/ubuntu:14.04 foobar
[root@docker supervisor]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/ubuntu 14.04 6e4f1fe62ff1 4 months ago 197 MB
foobar latest 6e4f1fe62ff1 4 months ago 197 MB
[root@docker supervisor]# docker tag ubuntu:14.04 foobar:cookbook
[root@docker supervisor]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/ubuntu 14.04 6e4f1fe62ff1 4 months ago 197 MB
foobar cookbook 6e4f1fe62ff1 4 months ago 197 MB
foobar latest 6e4f1fe62ff1 4 months ago 197 MB
- 正确设置标签镜像
[root@docker supervisor]# docker tag -h
Usage: docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]
将镜像发布到Docker Hub
- 在登录过程中,会将Docker Hub凭据保存到文件中
- 账号:danche1013
[root@docker ~]# docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username (danche1013): danche1013
Password:
Login Succeeded
[root@docker ~]# cat .docker/config.json
{
"auths": {
"https://index.docker.io/v1/": {
"auth": "..."
}
}
# 给镜像打标签
[root@docker ~]# docker tag flask how2dock/flask
# 上传镜像,需要注意镜像名遵循命令规则
# $ docker push <hub-user>/<repo-name>:<tag>
[root@docker ~]# docker tag danche1013/how2dock/flask danche1013/study:how2dock_flask
[root@docker ~]# docker push danche1013/study:how2dock_flask
The push refers to a repository [docker.io/danche1013/study]
f786468a7bca: Pushing [==================================================>] 2.56 kB
420c10a558b8: Pushing [==================================================>] 4.107 MB
c7e9a837d262: Pushing 3.584 kB
b0261a6321e2: Pushing [===> ] 7.582 MB/121.6 MB
e8304754711e: Waiting
b2463481acc8: Waiting
7f8323ac7756: Waiting
3da511183950: Waiting
48dc77435ad5: Waiting
f2fa9f4cf8fd: Waiting
ONBUILD镜像
- ONBUILD指令定义了一个会在未来执行的触发器.这个触发器是一个常规Dockerfile指令,比如RUN或ADD.包含ONBUILD指令的镜像称之为父镜像.当一个父镜像被用作基础镜像时(即通过FROM指令被引用),新镜像也被称为子镜像,子镜像构建过程中会触发在父镜像ONBUILD中定义的指令
- 换句话说,父镜像会指定子镜像在构建时需要做些什么
- 仍然可以在子镜像的Dockerfile文件中添加指令,但是父镜像中ONBUILD指令的内容会最先执行
# 案例
FROM node:0.12.6
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
ONBUILD COPY package.json /usr/src/app
ONBUILD RUN npm install
ONBUILD COPY . /usr/src/app
CMD ["npm","start"]
# 子镜像调用
FROM node:0.12.6-onbuild
# 在构建这个子镜像时,Docker会自动将package.json文件从本地复制到/usr/src/app
运行私有registry
- 使用Docker registry镜像创建一个容器.这样就拥有一个私有的registry
[root@docker ~]# docker pull registry:2
[root@docker ~]# docker run -d -p 5000:5000 docker.io/registry:2
5f2b8888025efe5631981f2781cef602360f31203ebea51179a9d4f3a8ef982d
[root@docker ~]# curl -i http://localhost:5000/v2/
HTTP/1.1 200 OK
Content-Length: 2
Content-Type: application/json; charset=utf-8
Docker-Distribution-Api-Version: registry/2.0
X-Content-Type-Options: nosniff
Date: Sat, 09 May 2020 13:58:59 GMT
# 将镜像推送到私有仓库
[root@docker ~]# docker tag busybox localhost:5000/busy
[root@docker ~]# docker push localhost:5000/busy
The push refers to a repository [localhost:5000/busy]
5b0d2d635df8: Pushed
latest: digest: sha256:a2490cec4484ee6c1068ba3a05f89934010c85242f736280b35343483b2264b6 size: 527
- 如果从其他计算机访问私有仓库,会受到错误消息.编辑/etc/default/docker(Ubuntu)或/etc/docker/daemon.json(CentOS),Ubuntu添加:
DOCKER_OPTS="--insecure-registry <IP_OF_REGISTRY>:5000"
,CentOS添加{"insecure-registries": ["10.0.0.111:5000"]}
- 访问
http://10.0.0.180:5000/v2/_catalog
[root@docker ~]# curl http://10.0.0.180:5000/v2/_catalog
{"repositories":["busy","flask"]}
- 通过/v2/manifests/这个API来获得镜像的清单描述,其中
<name>
是镜像的名称,<reference>
是镜像的标签
[root@docker ~]# curl http://10.0.0.180:5000/v2/busy/manifests/latest
{
"schemaVersion": 1,
"name": "busy",
"tag": "latest",
"architecture": "amd64",
"fsLayers": [
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:e2334dd9fee4b77e48a8f2d793904118a3acf26f1f2e72a3d79c6cae993e07f0"
}
],
- 其中每一个blob都代表一个镜像层.可以通过registry API来进行上传、下载或删除blob操作
- 通过/v2/tags/listURI列出一个镜像的所有标签
[root@docker ~]# curl http://10.0.0.180:5000/v2/flask/tags/list
{"name":"flask","tags":["latest"]}
[root@docker ~]# docker tag flask localhost:5000/flask:foobar
[root@docker ~]# docker push localhost:5000/flask:foobar
The push refers to a repository [localhost:5000/flask]
f786468a7bca: Layer already exists
420c10a558b8: Layer already exists
c7e9a837d262: Layer already exists
b0261a6321e2: Layer already exists
e8304754711e: Layer already exists
b2463481acc8: Layer already exists
7f8323ac7756: Layer already exists
3da511183950: Layer already exists
48dc77435ad5: Layer already exists
f2fa9f4cf8fd: Layer already exists
foobar: digest: sha256:7151e122a35714bafe3852585cd309c1c32ac181eb63c8e36fb407821e2cb69a size: 2417
[root@docker ~]# curl http://10.0.0.180:5000/v2/flask/tags/list
{"name":"flask","tags":["latest","foobar"]}