查看未运行的的container
docker ps -f "status=exited"
或
docker container ls -f "status=exited"
docker commit命令
docker commit: 从容器创建一个新的镜像。
OPTIONS说明:
-
a: 提交的镜像作者;
-
c: 使用Dockerfile指令来创建镜像;
-
m: 提交时的说明文字;
-
p: 在commit时,将容器暂停。
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
Dockerfile
#
Dockerfile注释
FROM 文件的开始
- FROM scratch # 从头开始制作一个最简单的镜像基础
- FROM centos # 使用centos作为系统,没有则自动拉取
- FROM centos:7.0 # 指定系统版本
LABEL 注释
- LABEL version=“1.0”
- LABEL author=“sxj”
RUN 执行命令,每执行一次,镜像就多一层
- RUN yum -y update
- RUN yum install xxx
由于每次RUN都会多一层,建议将上面结合起来写:
RUN yum -y update && yum -y install xxx
WORKDIR 进入或创建目录
- WORKDIR /root # 进入/root目录
- WORKDIR /haha # 创建/haha目录
ADD and COPY 将本地文件加入image
- ADD可以解压缩文件
- ADD hello / # 添加hello到根目录
- ADD xxx.tar.gz / # 添加并解压到跟目录
- COPY hello / # 添加hello到根目录
ENV 定义镜像的环境变量
- ENV MYSQL_VERSION 5.6 # 设置常量
- RUN apt-get -y install mysql-server=”${MYSQL_VERSION}” # 安装5.6版本mysql
分享docker image到docker hub
https://hub.docker.com/
注:image名字一定要以自己docker hub的用户名开头
// 登录
docker login
// 推送镜像
docker image push scoola/diff
// 拉取镜像
docker pull scoola/diff
分享Dockerfile
分享docker image不如分享Dockerfile,更加安全。
docker 更换源
https://www.cnblogs.com/zpchcbd/p/11696697.html
$ cd /etc/docker
$ cat daemon.json
{
"registry-mirrors": [
"https://kfwkfulq.mirror.aliyuncs.com",
"https://2lqq34jg.mirror.aliyuncs.com",
"https://pee6w651.mirror.aliyuncs.com",
"https://registry.docker-cn.com",
"http://hub-mirror.c.163.com"
],
"dns": ["8.8.8.8","8.8.4.4"]
}
然后重启docker
systemctl docker restart
docker tag
标记本地镜像,将其归入某一仓库。
将镜像ubuntu:15.10标记为 runoob/ubuntu:v3 镜像:
root@runoob:~# docker tag ubuntu:15.10 runoob/ubuntu:v3
root@runoob:~# docker images runoob/ubuntu:v3
REPOSITORY TAG IMAGE ID CREATED SIZE
runoob/ubuntu v3 4e3b13c8a266 3 months ago 136.3 MB
搭建私有docker registry仓库
https://hub.docker.com/_/registry
查看私有仓库日志:
浏览器访问:
http://127.0.0.1:5000/v2/_catalog
也可搭建
Harbor
,具有web管理界面, 可参考
Harbor介绍文档
。
Harbor和Registry的比较
Harbor和Registry都是Docker的镜像仓库,但是Harbor作为更多企业的选择,是因为相比较于Regisrty来说,它具有很多的优势。
1.提供分层传输机制,优化网络传输
Docker镜像是是分层的,而如果每次传输都使用全量文件(所以用FTP的方式并不适合),显然不经济。必须提供识别分层传输的机制,以层的UUID为标识,确定传输的对象。
2.提供WEB界面,优化用户体验
只用镜像的名字来进行上传下载显然很不方便,需要有一个用户界面可以支持登陆、搜索功能,包括区分公有、私有镜像。
3.支持水平扩展集群
当有用户对镜像的上传下载操作集中在某服务器,需要对相应的访问压力作分解。
4.良好的安全机制
企业中的开发团队有很多不同的职位,对于不同的职位人员,分配不同的权限,具有更好的安全性。
5.Harbor提供了基于角色的访问控制机制,并通过项目来对镜像进行组织和访问权限的控制。
kubernetes中通过namespace来对资源进行隔离,在企业级应用场景中,通过将两者进行结合可以有效将kubernetes使用的镜像资源进行管理和访问控制,增强镜像使用的安全性。尤其是在多租户场景下,可以通过租户、namespace和项目相结合的方式来实现对多租户镜像资源的管理和访问控制。
同时docker registry的一些缺陷:
- 缺少认证机制,任何人都可以随意拉取及上传镜像,安全性缺失
- 缺乏镜像清理机制,镜像可以push却不能删除,日积月累,占用空间会越来越大
- 缺乏相应的扩展机制
docker容器限制
# 限制内存
docker run --memory=100M ubuntu
# 设置占有cpu相对权重
docker run --cpu-shares=1 ubuntu
docker进入容器
docker attach
docker attach可以attach到一个已经运行的容器的stdin,然后进行命令执行的动作。
但是需要注意的是,如果从这个stdin中exit,会导致容器的停止。
docker exec
在docker 里面新开了一个bash 进程,在该终端可以通过命令和容器交互,类似于通过ssh和远程linux server 交互。
docker exec -it 0e63f8c3d293 /bin/sh
使用-it时,则和我们平常操作console界面类似。而且也不会像attach方式因为退出,导致整个容器退出。
结束所有镜像
docker stop $(docer ps -aq)
# 其中docker ps -aq 列出所有容器ID(CONTAINER ID)
docker inspect
获取容器/镜像的元数据。
docker inspect [OPTIONS] NAME|ID [NAME|ID…]
OPTIONS说明:
- -f :指定返回值的模板文件。
- -s :显示总的文件大小。
- –type :为指定类型返回JSON。
docker network
-
docker network inspect
在一个或多个网络上显示详细信息
# 桥接信息
docker network inspect bridge
-
docker network connect
将容器连接到网络
-
docker network create
创建一个网络
-
docker network disconnect
从网络断开容器
-
docker network inspect
显示一个或多个网络的详细信息
-
docker network ls
列出网络
-
docker network prune
删除所有未使用的网络
-
docker network rm
删除一个或多个网络
docker run –link
可以用来链接2个容器,使得源容器(被链接的容器)和接收容器(主动去链接的容器)之间可以互相通信,并且接收容器可以获取源容器的一些数据,如源容器的环境变量。
举例:
# 环境: 已启动一个名为redis的container提供缓存服务。
[root@centos7 compose]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3781aa66ff19 redis "docker-entrypoint..." 12 minutes ago Up 12 minutes 0.0.0.0:6379->6379/tcp redis
# 将新新启动的名为 web_incr 的container链接到 redis container
# 并通过-e命令设置环境变量RDIS_HOST=redis container
# -d 容器后台运行
# -p 端口映射
docker run -d --link redis -p 6000:9999 --name web_incr -e REDIS_HOST=redis web_incr
# 这样启动链接后,就可以在代码中直接通过获取环境变量来链接redis,而不需要写死redis服务地址
尝试访问,可发现num有增加:
[root@centos7 compose]# curl http://127.0.0.1:6000/
Hello! Incr num is 25, hostname:a73f0e217f6f
[root@centos7 compose]# curl http://127.0.0.1:6000/
Hello! Incr num is 26, hostname:a73f0e217f6f
[root@centos7 compose]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a73f0e217f6f web_incr "/bin/sh -c 'pytho..." About a minute ago Up About a minute 0.0.0.0:6000->9999/tcp web_incr
且可以发现,返回的hostname即为web_incr的CONTAINER ID
web_incr代码如下:
from flask import Flask
from redis import Redis
import socket
import os
app = Flask(__name__)
redis = Redis(host=os.environ.get('REDIS_HOST', '127.0.0.1'), port=6379)
@app.route('/')
def hello():
redis.incr('hits')
return 'Hello! Incr num is {}, hostname:{}'.format(int(redis.get('hits')),
socket.gethostname())
if __name__ == "__main__":
app.run(host="0.0.0.0", port=9999, debug=True)
web_incr Dockerfile:
FROM python
ENV APP_ROOT=/src/code
WORKDIR ${APP_ROOT}/
COPY . ${APP_ROOT}/
RUN pip install Flask -i https://pypi.tuna.tsinghua.edu.cn/simple
RUN pip install redis -i https://pypi.tuna.tsinghua.edu.cn/simple
CMD python main.py
Docker Compose
经过上述通过–link,我们的web_incr容器可以访问redis容器,但部署很麻烦,如果仅仅2个容器尚可接受,若需要部署的容器更多,那就需要Docker compose来帮我们实现了。
安装Docker Compose:
1.下载
curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
2.增加执行权限
chmod +x /usr/local/bin/docker-compose
创建docker-compose.yaml
[root@centos7 compose]# ls
docker-compose.yml Dockerfile main.py
[root@centos7 compose]# cat docker-compose.yml
# yaml 配置实例
version: '3'
services:
web:
build: .
ports:
- "6000:9999"
links:
- redis
environment:
REDIS_HOST: redis
redis:
image: redis
通过docker-compose启动redis和web_incr容器:
# 注:该命令必须在docker-compose.yml路径下运行
[root@centos7 compose]# docker-compose up -d
Creating network "compose_default" with the default driver
Building web
Step 1/7 : FROM python
---> 28a4c88cdbbf
Step 2/7 : ENV APP_ROOT /src/code
---> Using cache
...省略镜像构建
Creating compose_redis_1 ... done
Creating compose_web_1 ... done
# 可发现2个容器都已运行,且可以发现多了一个名为compose_web的镜像
[root@centos7 compose]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
compose_web latest 333f57c75b05 3 minutes ago 892 MB
web_incr latest 77ad408fd8a3 20 minutes ago 892 MB
# 查看容器,可以看到新启动了名为compose_web和redis的容器:
[root@centos7 compose]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b461e70356e2 compose_web "/bin/sh -c 'pytho..." 5 minutes ago Up 5 minutes 0.0.0.0:6000->9999/tcp compose_web_1
e342d72d2109 redis "docker-entrypoint..." 5 minutes ago Up 5 minutes 6379/tcp compose_redis_1
# 请求查看,可正常运行,且hostname与compose_web的CONTAINER ID 相同。
[root@centos7 compose]# curl http://127.0.0.1:6000/
Hello! Incr num is 1, hostname:b461e70356e2
[root@centos7 compose]# curl http://127.0.0.1:6000/
Hello! Incr num is 2, hostname:b461e70356e2
至此,我们已经通过docker-compose实现了快速部署redis和web_incr的服务。
但是,实际使用中,若我们想动态扩容web_incr方法还是很麻烦。为了解决这个问题,我们可以引入负载均衡,随后就可以借助docker-compose实现动态扩容。
首先我们将之前启动的容器停止:
[root@centos7 compose]# docker-compose stop
Stopping compose_web_1 ... done
Stopping compose_redis_1 ... done
修改Dockerfile,增加端口暴露容器端口Expose:
FROM python
ENV APP_ROOT=/src/code
WORKDIR ${APP_ROOT}/
COPY . ${APP_ROOT}/
RUN pip install Flask -i https://pypi.tuna.tsinghua.edu.cn/simple
RUN pip install redis -i https://pypi.tuna.tsinghua.edu.cn/simple
EXPOSE 80
CMD python main.py
删除旧compose_web镜像,下次docker-compose up时会重新打包镜像:
docker rmi compose_web
修改main.py端口为80(不改为80会导致负载均衡失败)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=80, debug=True)
修改后的docker-compose.yaml:
version: '3'
services:
web:
build: .
links:
- redis
environment:
REDIS_HOST: redis
redis:
image: redis
lib:
image: dockercloud/haproxy
ports:
- 7777:80
links:
- web
volumes:
- /var/run/docker.sock:/var/run/docker.sock
启动并测试:
# 启动
[root@centos7 compose]# docker-compose up -d
Creating network "compose_default" with the default driver
Creating compose_redis_1 ... done
Creating compose_web_1 ... done
Creating compose_lib_1 ... done
# 查看容器
[root@centos7 compose]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ec554f11a680 dockercloud/haproxy "/sbin/tini -- doc..." 5 minutes ago Up 5 minutes 443/tcp, 1936/tcp, 0.0.0.0:7777->80/tcp compose_lib_1
60ea95907902 compose_web "/bin/sh -c 'pytho..." 5 minutes ago Up 5 minutes 80/tcp compose_web_1
b1bdff3e70e7 redis "docker-entrypoint..." 5 minutes ago Up 5 minutes 6379/tcp compose_redis_1
# 测试
[root@centos7 compose]# curl http://127.0.0.1:7777/
Hello! Incr num is 1, hostname:60ea95907902
[root@centos7 compose]# curl http://127.0.0.1:7777/
Hello! Incr num is 2, hostname:60ea95907902
容器扩容
[root@centos7 compose]# docker-compose up --scale web=3 -d
compose_redis_1 is up-to-date
Starting compose_web_1 ... done
Creating compose_web_2 ... done
Creating compose_web_3 ... done
compose_lib_1 is up-to-date
[root@centos7 compose]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
761485161b6f compose_web "/bin/sh -c 'pytho..." 22 seconds ago Up 21 seconds 80/tcp compose_web_3
af8e33b80771 compose_web "/bin/sh -c 'pytho..." 22 seconds ago Up 21 seconds 80/tcp compose_web_2
ec554f11a680 dockercloud/haproxy "/sbin/tini -- doc..." 8 minutes ago Up 8 minutes 443/tcp, 1936/tcp, 0.0.0.0:7777->80/tcp compose_lib_1
60ea95907902 compose_web "/bin/sh -c 'pytho..." 8 minutes ago Up 8 minutes 80/tcp compose_web_1
b1bdff3e70e7 redis "docker-entrypoint..." 8 minutes ago Up 8 minutes 6379/tcp compose_redis_1
访问测试,可发现每次hostname对应不同容器CONTAINER ID:
[root@centos7 compose]# curl http://127.0.0.1:7777/
Hello! Incr num is 3, hostname:60ea95907902
[root@centos7 compose]# curl http://127.0.0.1:7777/
Hello! Incr num is 4, hostname:af8e33b80771
[root@centos7 compose]# curl http://127.0.0.1:7777/
Hello! Incr num is 5, hostname:761485161b6f
[root@centos7 compose]# curl http://127.0.0.1:7777/
Hello! Incr num is 6, hostname:60ea95907902
[root@centos7 compose]# curl http://127.0.0.1:7777/
Hello! Incr num is 7, hostname:af8e33b80771
容器减少至1个:
[root@centos7 compose]# docker-compose up --scale web=1 -d
compose_redis_1 is up-to-date
Stopping and removing compose_web_2 ... done
Stopping and removing compose_web_3 ... done
Starting compose_web_1 ... done
compose_lib_1 is up-to-date
[root@centos7 compose]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ec554f11a680 dockercloud/haproxy "/sbin/tini -- doc..." 10 minutes ago Up 10 minutes 443/tcp, 1936/tcp, 0.0.0.0:7777->80/tcp compose_lib_1
60ea95907902 compose_web "/bin/sh -c 'pytho..." 10 minutes ago Up 10 minutes 80/tcp compose_web_1
b1bdff3e70e7 redis "docker-entrypoint..." 10 minutes ago Up 10 minutes 6379/tcp compose_redis_1
依然可以正常访问,且hostname无变化:
[root@centos7 compose]# curl http://127.0.0.1:7777/
Hello! Incr num is 9, hostname:60ea95907902
[root@centos7 compose]# curl http://127.0.0.1:7777/
Hello! Incr num is 10, hostname:60ea95907902
[root@centos7 compose]# curl http://127.0.0.1:7777/
Hello! Incr num is 11, hostname:60ea95907902
docker run -v
作用:挂载宿主机的一个目录
docker run -v /宿主机目录:/容器目录 镜像名
练习:
- 将mysql容器目录挂载到宿主机
- 在容器内创建数据
- 删除该容器
- 运行新的mysql容器,并挂载之前的宿主目录
- 查看在旧容器创建的数据,可发现数据依然在
# 1.启动名为mysql_test容器
# -e MYSQL_ALLOW_EMPTY_PASSWORD=true设置mysql无密码
[root@centos7 test]# docker run -d -v mysql:/var/lib/mysql --name mysql_test -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql
9c30284048ed314f0cce950b7d7d5a421b0caeb755d34524ad411e81cadf6155
[root@centos7 test]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9c30284048ed mysql "docker-entrypoint..." About a minute ago Up 58 seconds 3306/tcp, 33060/tcp mysql_test
# 查看挂载路径及新建mysql挂载目录
[root@centos7 volumes]# pwd
/var/lib/docker/volumes
[root@centos7 volumes]# ls | grep mysql
mysql
# 2.进入容器,创建名为docker的database数据
[root@centos7 test]# docker exec -it mysql_test /bin/bash
root@9c30284048ed:/# mysql -u root
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.24 MySQL Community Server - GPL
mysql> create database docker;
Query OK, 1 row affected (0.05 sec)
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| docker |
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.01 sec)
# 3.删除该容器
[root@centos7 test]# docker stop mysql_test
mysql_test
[root@centos7 test]# docker rm mysql_test
mysql_test
# 4.运行新的mysql容器,并挂载之前的宿主目录
[root@centos7 test]# docker run -d -v mysql:/var/lib/mysql --name mysql_test2 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql
c02cde00724df9630d5bc3088c1ae2486e38108033f57d34f73f7ac834be267f
[root@centos7 test]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c02cde00724d mysql "docker-entrypoint..." 5 seconds ago Up 4 seconds 3306/tcp, 33060/tcp mysql_test2
# 5.进入新建容器mysql_test2,查看名为docker的database是否存在
[root@centos7 test]# docker exec -it mysql_test2 /bin/bash
root@c02cde00724d:/# mysql -u root
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.24 MySQL Community Server - GPL
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| docker |
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.02 sec)
Dockerfile Volume指令与docker -v的区别
Dockerfile中的VOLUME指令实际使用中是不是就是跟docker run中的-v参数一样是将宿主机的一个目录绑定到容器中的目录以达到共享目录的作用呢?
并不然,其实VOLUME指令只是起到了声明了容器中的目录作为匿名卷,但是并没有将匿名卷绑定到宿主机指定目录的功能。
但是当我们生成镜像的Dockerfile中以Volume声明了匿名卷,并且我们以这个镜像run了一个容器的时候,docker会在安装目录下的指定目录下面生成一个目录来绑定容器的匿名卷(这个指定目录不同版本的docker会有所不同)。
简单来说,就是我们在docker run 没有显示-v指定挂载时,会自动将Dockerfile中Volume指定目录挂载在/var/lib/docker/volumes目录下
举例:
FROM java:8
VOLUME /tmp /usr/tmp # VOLUME /tmp /usr/tmp将容器中的 /tmp 和/usr/tmp目录映射到宿主机的目录
COPY /target/springbootdemo.jar app.jar
RUN bash -c 'touch /app.jar'
EXPOSE 10001
ENTRYPOINT ["java","-jar","/app.jar"]
运行后:
docker inspect add8a379065c
"Mounts": [
{
"Type": "volume",
"Name": "3c18486ccfe419156ef62d346d29f6668ec34a236497c47f7e7907e95c310d0e",
"Source": "/var/lib/docker/volumes/3c18486ccfe419156ef62d346d29f6668ec34a236497c47f7e7907e95c310d0e/_data",
"Destination": "/tmp",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
},
{
"Type": "volume",
"Name": "c5b6e1e0f6db6e70f15d836caa9663173e4bd8e23db1817146c31348d931a43b",
"Source": "/var/lib/docker/volumes/c5b6e1e0f6db6e70f15d836caa9663173e4bd8e23db1817146c31348d931a43b/_data",
"Destination": "/usr/tmp",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
]