ocker官网教程(学习一样新东西最快的方法是去看官方文档,讲解的清楚,教程简单,但是包括的面十分的广,只记录自己觉得有用的命令,详细请去官网查看Docker start)
在docker的学习过程中我也其中也发现一篇很好的教程,简洁明了,我也在这里学到了很多,也摘录总结在了这里,推荐大家去Docker--从入门到实践进行学习。也可以fork那个项目下来进行贡献。如果网速有问题可以docker一个,直接本地访问(教程提供的方法,我只是搬运一下)
$ docker pull dockerpracticecn/docker_practice$ docker run -it --rm -p4000:80dockerpracticecn/docker_practice
访问127.0.0.1:4000即可
手册内容
Docker的安装(老规矩)
普通安装方式
sudo apt-get update
sudo apt-get install docker.io
但是上面的下载的版本会比较低,所以建议用下面这种方式
sudo apt-get install curl
curl -sSLhttps://get.docker.com/| sh
下载完毕启动Docker的守护进程
sudo service docker start
检查Docker是否安装成功(这是运行了一个容器,后面会有解释)
sduo docker run hello-world
如果最终提示Hello from Docker! 如图所示则表明docker安装成功
安装成功提示
Docker试水(初级篇)
10分钟小任务认识Docker
查看版本号
docker version
版本号
查找镜像 (镜像的全称是/)
docker search tutorial
或者可以尝试
docker search ubuntu
下载镜像
docker pull learn/tutorial
用docker run来创建和运行docker容器(docker可以创建容器并在容器中运行指定的命令)
docker run learn/tutorial echo "hello world"
查看所有容器(加上 -l 可以查看最新创建的容器)
docker ps
运行并修改容器(给原容器安装ping)
docker run learn/tutorial apt-get install -y ping
通过docker ps -l找出安装过ping包的容器的ID号(-l 是last,找出最新创建的容器)
docker ps -l
查看容器
将安装过ping包的容器提交为新的镜像,这时会返回一个新的ID便是新生成的镜像的ID(意思是把容器打包成镜像,因为平时都是把镜像打开变成容器,这里相当于我们对容器进行了修改,想让更多的人来用,所以我们把我们修改后的容器变成了镜像。镜像易于传播,但是容器不行,)
docker commit {containId} {new image name}
docker commit 09c2e9353f01 yugougou/ping
基于新的镜像创建容器并在容器中执行 pingwww.google.com这条指令(新镜像要使用全名 yugougou/ping)
docker run yugougou/ping pingwww.google.com
查询容器信息
docker ps 查询所有运行的容器
docker inspect a102 查看单个容器的信息(根据docker ps列出的容器名,选取前三四个字符即可)
新镜像上传仓库
docker images 查询本机的镜像列表
查询结果中有yugoguou/image这个镜像
查询结果
所以把镜像推送到Docker官仓(这里有个权限的要求,要求你要在docker hub上有注册过 并输入用户名密码才可以进行上传)
docker push yugougou/ping
Docker试水 中级篇
创建镜像
首先说明一下 docker中有几个比较重要的概念,『镜像』,『镜像标签』,『容器』,『镜像仓库』,『镜像仓库源』。我简单说下自己的理解。
镜像就是一个模子,我们可以通过这个模子(镜像)来创建成品(容器),相同的模子(镜像)创建的成品(容器)都是一样的,而且不同的模子(镜像)都是有编号(镜像标签)的。我们可以把我们的模子(镜像)放存放到仓库(镜像仓库)中,这样其他人也可以用。而且呢,仓库(镜像仓库)一般都是放在一个大的厂子(镜像仓库源)里来管理的,以此保证我们的模子(镜像)不会因为太多而管理混乱。
所以总结一下就是:我们可以在这个工厂(镜像仓库源)的仓库(镜像仓库)中根据标签(镜像标签)拿到我们需要的模子(镜像),来制作生成我们的成品(容器)。
比如我在生成好自己的镜像并且想要push到镜像仓库的时候,就可以通过地址来看清这几者的关系
docker push index.alauda.cn/alaudaorg/zpyuregis:syncimagealaudaindex.docker.io /yugougou/get-started1:zpyutestsyncREGISTORY REPOSITORY TAG
index.docker.io对应镜像源
/yugougou/get-started1 对应镜像仓库
:zpyutestsync 对应镜像标签
这样我就把自己的镜像push到了dockerhub中用户名为yugougou的get-started1的仓库中,而且为了标识清楚这个镜像,我给这个镜像起的一个名字就是zpyutestsync.
相信通过以上的详细介绍你应该还是没有明白这些概念。。。。,如果真的不明白请再看一遍,如果真的还是不明白,就请动手敲一下代码,接下来我就教大家来创建镜像,启动一个服务,这样应该就会更好的理解了。
现在我们知道了镜像与容器的关系,知道了容器是由镜像run起来的,那么镜像是怎么来的呢,我们可不可以拥有自己的镜像呢,当然可以,通过dockerfile我们就可以来搭建自己的镜像了,我们先创建一下三个文件:
Dockerfile
# Use an official Python runtime as a parent imageFROM python:2.7-slim# Set the working directory to /appWORKDIR /app# Copy the current directory contents into the container at /appADD . /app# Install any needed packages specified in requirements.txtRUN pip install --trusted-host pypi.python.org -r requirements.txt# Make port 80 available to the world outside this containerEXPOSE 80# Define environment variableENV NAME World# Run app.py when the container launchesCMD ["python","app.py"]
requirements.txt
Flask
Redis
app.py
fromflaskimportFlaskfromredisimportRedis, RedisErrorimportosimportsocket# Connect to Redisredis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)app = Flask(__name__)@app.route("/")defhello():try: visits = redis.incr("counter")exceptRedisError: visits ="cannot connect to Redis, counter disabled"html ="
Hello {name}!
"\"Hostname: {hostname}"\"Visits: {visits}"returnhtml.format(name=os.getenv("NAME","world"), hostname=socket.gethostname(), visits=visits)if__name__ =="__main__": app.run(host='0.0.0.0', port=80)
创建app(注意有个点)
docker build -t friendlyhello .
找到新建的镜像
$ docker imagesREPOSITORY TAG IMAGE IDfriendlyhello latest326387cea39
跑起来
docker run -d -p 4000:80 friendlyhello
访问http://localhost:4000即可,这样你就有了一个flask框架的小的web应用程序了,真是TM的吃了一鲸,对的,就是这么快。好的这个过程我们来慢慢分解一下,当然需要你有一些python开发的基础。
先说一下这里我们用到的几个文件,Dockerfile、requirements.txt、app.py,其实里面与镜像构建相关的只有Dockerfile,另外两个文件只是用来让这个demo更炫酷一些而已,其实一个Dockerfile就完全可以制作一个镜像了。我们看下Dockerfile里的内容:
# Use an official Python runtime as a parent imageFROM python:2.7-slim# Set the working directory to /appWORKDIR /app# Copy the current directory contents into the container at /appADD . /app# Install any needed packages specified in requirements.txtRUN pip install --trusted-host pypi.python.org -r requirements.txt# Make port 80 available to the world outside this containerEXPOSE 80# Define environment variableENV NAME World# Run app.py when the container launchesCMD ["python","app.py"]
其实我想如果你英文稍微好一些,应该大概能明白是什么意思,FROM的作用就是说我要做的这个镜像是基于哪个基础镜像的,这里我们的镜像是基于python:2.7-slim这个基础镜像的,所以我们这个镜像里生来就有python的环境啦。
看 我没骗你吧
WORKDIR是说我的工作目录在哪里
image.png
(未完待续。。。这几天持续更新重新整理该文章)
好不容易创建了一个镜像,想分享一下,肿么办
登录
docker login
打标签
docker tag image username/repository:tag
举个例子
docker tag image yugougou/get-started:part2
这里的于狗狗是你的用户名,get-started是仓库名
公布镜像
docker push username/repository:tag
例如
docker push yugougou/get-started:part2
测试(拉一把 看看能不能跑起来)
docker run -p 4000:80 username/repository:tag
例如一个
docker run -p 4000:80 yugougou/get-started:part2
如果能成功跑起来,那就没什么问题了
服务
要是想要起服务的话,那就要用上编排工具了 docker-compose.yml
docker-compose.yml(里面有services还有networks)
version:"3"services: web:# replace username/repo:tag with your name and image detailsimage: yugougou/get-started:part2 deploy: replicas: 5 resources: limits: cpus:"0.1"memory: 50M restart_policy: condition: on-failure ports: -"80:80"networks: - webnetnetworks: webnet:
启动第一个负载均衡的app,肯定是少不了要用集群的,这里使用的镜像就是上一节我们制作的镜像。
docker swarm init
启动它 给他起一个漂亮的名字
docker stack deploy -c docker-compose.yml getstartedlab
看看我们起的服务如何
docker service ls
yugougou@yugougoudeMacBook-Pro:~/Desktop$dockerservice lsID NAME MODE REPLICAS IMAGE PORTS9cinwb60b78b getstartedlab_web replicated 10/10 yugougou/get-started:part2 *:80->80/tcp
看看服务里的任务有哪些吧
docker service ps getstartedlab_web
yugougou@yugougoudeMacBook-Pro:~/Desktop$dockerservice ps getstartedlab_webID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTStgbba2qixjoa getstartedlab_web.1 yugougou/get-started:part2 moby Running Running 6 minutes agoeg9oo085u2ud getstartedlab_web.2 yugougou/get-started:part2 moby Running Running 6 minutes agoyjc31x8ac2o0 getstartedlab_web.3 yugougou/get-started:part2 moby Running Running 6 minutes agodmoye5s5n09x getstartedlab_web.4 yugougou/get-started:part2 moby Running Running 6 minutes agoq16fo41swnai getstartedlab_web.5 yugougou/get-started:part2 moby Running Running 6 minutes agoidkc65whadin getstartedlab_web.6 yugougou/get-started:part2 moby Running Running 6 minutes agos6k7b8o81m35 getstartedlab_web.7 yugougou/get-started:part2 moby Running Running 6 minutes agortod8scwkwt7 getstartedlab_web.8 yugougou/get-started:part2 moby Running Running 6 minutes agoz29qubyqa29b getstartedlab_web.9 yugougou/get-started:part2 moby Running Running 6 minutes agopbq7qr4e8jhv getstartedlab_web.10 yugougou/get-started:part2 moby Running Running 6 minutes ago
只查看端口
docker ps -q
哎呀 发现之前的编排文件dockerfile里的副本主机起的不够,肿么办,面对这种状况,分两步,第一步打开冰箱,不是 第一步修改dockerfile,然后重启喽,记得名字不要写错
yuzhipeng@yuzhipengdeMacBook-Pro:~/Desktop$dockerstack deploy -c docker-compose.yml getstartedlabUpdating service getstartedlab_web (id: 9cinwb60b78bmon03mjakt6mb)
其实我什么也没有改--__--
关掉app(关掉之前用docker service ls 看看)
docker stack rm getstartedlab
(关掉之前用docker service ls 看看)
砸了集群
docker swarm leave --force
(再用docker service ls看看)
集群 Swarms
大家好 我是分隔符
docker run hello-world
docker --version
docker pull --help
运行jenkins(需要制定端口):
docker run --name myjenkins -p 8080:8080 index.alauda.cn/alaudaorg/alauda-jenkins
查看:
docker ps -a | grep jenkins
进入docker
docker exec -ti c3c bash
编辑docker
cat /var/run/docker.sock
docker login index.alauda.cn
docker-compose up -d
拉取并运行镜像: docker run --name webserver -d -p 80:80 nginx
删除容器: docker container rm webserver
停止容器:docker stop containername
进入容器修改: docker exec -i -t webserver bash
今天要总结一下近期来使用学习docker的经验,很早之前就想来摸索一下docker,但是前些日子一直在忙于找工作,没什么时间,但是现在找到的公司做的是基于docker平台开发paas服务的公司,所以借机好好刷一把docker
体验Docker
使用docker指令创建 启动几个Docker应用,比如WordPress,gitlab服务
通过搭建WordPress来试试Docker
这几个应用下载的话会比较慢 要等几分钟
sudo docker run --name db --env MYSQL_ROOT_PASSWORD=example -d mariadb
sudo docker run --name MyWordPress --link db:mysql -p 8080:80 -d wordpress
--name参数创建了两个Docker容器,db和MyWordPress 通过docker ps可以查到名字
通过ifconfig查看本机的IP地址,在本机地址后加上端口号8080,会有惊喜。简直不能更爽
这个我在京东云上部署了一下,感觉用起来很快,部署效果非常好,比之前部署项目快的过,真的是几条命令就可以跑起来服务,厉害了访问
搭建Gitlab服务
首先启动postgresql
sudo docker run --name gitlab-postgresql -d --env'DB_NAME=gitlabhq_production'--env'DB_USER=gitlab'--env'DB_PASS=password'sameersbn/postgresql:9.4-12
然后启动redis
sudo docker run --name gitlab-redis -d sameersbn/redis:latest
最后启动gitlab
sudo docker run --name gitlab -d --link gitlab-postgresql:postgresql --link gitlab-redis:redisio --publish 10022:22 --publish 10080:80 --env'GITLAB_PORT=10080'--env'GITLAB_SSH_PORT=10022'--env'GITLAB_SECRETS_DB_KEY_BASE=long-and-random-alpha-numeric-string'sameersbn/gitlab:8.4.4
后来就可以访问端口10080即可进入gitlab,确实是太好用了,这时候用户名为root,密码是yu123456 连接(如果可用)点击进入
项目管理系统 Redmine
要使用saneersbn/redmine的镜像。两条指令
sudo docker run --name=postgresql-redmine -d --env='DB_NAME=redmine_production'--env='DB_USER=redmine'--env='DB_PASS=password'sameersbn/postgresql:9.4-12 docker run --name=redmine -d --link=postgresql-redmine:postgresql --publish=10083:80 --env='REDMINE_PORT=10083'sameersbn/redmine:3.2.0-4
Docker常用词
查看所有的镜像
docker images
查看相关进程
sudo docker ps
查看版本
docker version
ps只是看一些大体容器内容 inspect是看容器的详细内容
docker ps
docker inspect gitlab
docker push michelesr/ping
docker基础概念与常用命令
1.仓库、镜像、容器
2.基本指令
docker指令操作对象主要针对四个方面:
1 针对守护进程的系统资源设置和全局信息的获取: docker info, docker deamon
2 针对Docker仓库的查询,下载 : docker search, docker pull
3 针对docker镜像的查询,创建与删除: docker images, docker build
4 针对docker容器的查询,创建,开启,停止: docker ps, docker run
5 支持赋值,变量解析,嵌套
docker+命令关键字(command)+一系列参数([arg ])
例如
docker run --name MyWordPress --link db:mysql -p 8080:80 -d wordpress
基于wordpress镜像创建容器MyWordPress,通过docker ps可以查到名字MyWordPress,使用的镜像是woedpress的容器
获取帮助
docker command --help
Docker容器管理
容器标识符
每一个容器创建后都有一个CONTAINER ID作为容器的唯一的标识符,后续对容器的操作都是通过CONTAINER ID来完成,一般docker ps展示前16位,如果查询所有可以使用docker ps --no-trunc
查询容器状态
docker ps -a | grep d0131ae38d9d
停止容器
docker stop d0131ae38d9d
运行容器
docker start d0131ae38d9d
CONTAINER ID比较难记忆,所以创建容器时会有--name来给容器起一个别名 所以用别名也可以
查询容器信息(使用-f时可以用golang的模板提取出制定信息)
docker inspect -f {{.NetworkSettings.IPAddress}} MyWordPress
查询日志
docker logs MyWordPress
查询容器占用的系统资源
docker stats MyWordPress
容器内部命令
单容器
需求:登入Docker容器执行命令
方案: Docker提供原生的方式支持登入docker exec
docker exec + 容器名 + 容器内执行的命令
查看MyWordPress容器内的进程
docker exec MyWordPress ps aux
在容器内连续执行命令 加上 -it 参数即可,相当于以root身份登入,完成后通过 exit 退出
docker exec -it MyWordPress /bin/bash
多容器
Docker理念是‘一个容器一个进程’,如果一个服务由多个进程组成,就创建多个容器组成一个系统
在同一个主机下,docker run命令提供 ‘--link’建立容器之间的互联,而且他们是有顺序的,比如如果创建B容器的时候需要使用 ‘--link containerA’,那么创建B的时候,那么
在创建B容器的时候A容器必须已经创建且已经启动
对于WordPress,数据库容器(db)要先于Apache容器(MyWordPress)启动,所以启动方式应该是
docker start db
docker start MyWordPress
停止WordPress服务,先停止Apache容器(MyWordPress),再停止数据库容器(db),或者同时停止这两个容器
docker stop db MyWordPress
Docker compose
但是如果比较多的容器启动的话就会比较麻烦,本着偷懒的原则,Docker提供了一个容器编排工具--Docker Compose,允许用户用一个模板定义一组相关联的应用容器,会根据配置模板的‘--link’参数对启动的优先级进行排序,只用‘docker-compose up’一条语句即可将一个服务中多个容器依次创建与启动
安装 Docker compose:(在https://github.com/docker/compose/releases/download这里可以找到随时更新的命令)
curl -Lhttps://github.com/docker/compose/releases/download/1.18.0-rc2/docker-compose-`uname -s`-`uname -m`-o /usr/local/bin/docker-composechmod +x /usr/local/bin/docker-compose
使用1:
首先关掉WordPress的两个容器
docker stop db MyWordPress
创建一个文件夹 ~/wordpress,文件夹下创建docker-compose.yml的文件:
wordpress:
image: wordpress
links:
- db:mysql
ports:
- 8080:80
db:
image: mariadb
environment:
MYSQL_ROOT_PASSWORD: example
作用:创建了两个容器wordpress跟db,使用image指定了使用的镜像
创建启动WordPress服务
cd ~/wordpress &&docker-compose up
检测
开另一个命令终端, 输入docker ps查看容器
这是启动停止就变的简单了,
启动:
docker-compose start
停止:
docker-compose stop
删除原来的容器
查看所有容器(删除与未删除的)
docker ps -a
删除(根据名字(NAMES)来删除)
docker rm MyWordPress db
硬删的话就用 docker rm -f
使用2:gitlab改造
原来的用法
首先启动postgresqlsudo docker run --name gitlab-postgresql -d --env'DB_NAME=gitlabhq_production'--env'DB_USER=gitlab'--env'DB_PASS=password'sameersbn/postgresql:9.4-12然后启动redissudo docker run --name gitlab-redis -d sameersbn/redis:latest 最后启动gitlabsudo docker run --name gitlab -d --link gitlab-postgresql:postgresql --link gitlab-redis:redisio --publish 10022:22 --publish 10080:80 --env'GITLAB_PORT=10080'--env'GITLAB_SSH_PORT=10022'--env'GITLAB_SECRETS_DB_KEY_BASE=long-and-random-alpha-numeric-string'sameersbn/gitlab:8.4.4
改造结果
postgresql: image: sameersbn/postgresql:9.4-12environment: - DB_USER=gitlab - DB_PASS=password - DB_NAME=gitlabhq_productionredis: image: sameersbn/redis:latestgitlab: image: sameersbn/gitlab:8.4.4links: - redis:redisio - postgresql:postgresql ports: -"18008:80"-"18009:22"environment: - GITLAB_PORT=18008- GITLAB_SSH_PORT=18009- GITLAB_SECRETS_DB_KEY_BASE=long-and-random-alphaumeric-srting
创建一个项目~/gitlab,然后将上面放在docker-compose.yaml并放在该项目下
删除旧容器:
docker rm -f gitlab gitlab-redis gitlab-postgresql
启动新容器组
cd ~/gitlab/ && docker-compose up -d
登录
http://23.99.104.249:18008/
root/yu123456
Docker镜像管理
docker镜像查看
docker images -a
查看镜像分了多少层
docker history sameersbn/redis
方式:通过对容器的可写层修改来生成新镜像
问题:如果底层镜像出了问题,或者达到两个文件系统允许的最多层数(aufs最多支持128层)
解决方式: dockerfile
Docker 仓库管理
作用:镜像的存储,是镜像分发部署的关键
两种: 官方共有仓库 Docker hub,自己搭建私有仓库
共有仓库
上传镜像:
docker push ubuntu
搜索镜像:
docker search centos
下载镜像:
docker pull centos
私有仓库
使用docker-registry构建
方式1:
从Docker-registry拉取镜像
docker run -p 5000:5000 registry
Docker 网络和存储管理
Docker 网络
默认情况下使用网桥(bridge)+ NAT的通信模型
Docker在启动是默认自动创建网桥设备Docker0,同一个host的容器与容器之间可以通过docker0 通信
容器与外部网络之间通信使用NAT
Docker数据管理
Docker数据卷(data volume) 可以持久化数据,用于容器之间共享数据
docker可以在容器内部创建一个数据卷,但是在删除之后,如果没有其他容器引用该数据卷,对应的Host目录就会被删除。
不想被删除的话,可以挂载Host的目录到数据卷,作为容器的数据卷(将Host上的/data/volume1挂载容器中的 /volume1可以在Host与容器之间进行数据交换,这时容器中的数据可以写到volume1上,即使容器被删除,数据仍然保留到Host上)
挂载Host的文件作为数据卷
主要应用于Host与容器之间共享配置文件,否则每个配置文件一个镜像会造成镜像版本过多,管理不便
数据卷容器
备份、恢复和迁移数据卷
Docker 小点
容器:
按照 Docker 最佳实践的要求,容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用 数据卷、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。
数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器删除或者重新运行之后,数据却不会丢失。
运行镜像
$ docker run -it --rm \ubuntu:16.04\ bash
-it:这是两个参数,一个是 -i:交互式操作,一个是 -t 终端。我们这里打算进入 bash 执行一些命令并查看返回结果,因此我们需要交互式终端。
--rm:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 docker rm。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用 --rm 可以避免浪费空间。
ubuntu:16.04:这是指用 ubuntu:16.04 镜像为基础来启动容器。
bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 bash。
列出镜像
docker image ls
查看镜像、容器、数据卷所占用的空间
docker system df
删除镜像
docker image rm imageid
docker image rm $(docker image ls -q redis) //删除大法
一行码感受一下docker的威力
$ docker run --name webserver -d -p 80:80 nginx
Dockerfile
书写Dockerfile
touch Dockerfilevi Dockerfile//键入FROM nginxRUN echo'
Hello, Docker!
'>/usr/share/nginx/html/index.htmlDockerfile 中每一个指令都会建立一层,RUN 也不例外。每一个 RUN 的行为,就和刚才我们手工建立镜像的过程一样:新建立一层,在其上执行这些命令,执行结束后,commit 这一层的修改,构成新的镜像。
一个比较屌的镜像 (其实run指令里面的命令就是shell命令)
FROM debian:jessieRUN buildDeps='gcc libc6-dev make'\ && apt-get update \ && apt-get install -y$buildDeps\ && wget -O redis.tar.gz"http://download.redis.io/releases/redis-3.2.5.tar.gz"\ && mkdir -p /usr/src/redis \ && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \ && make -C /usr/src/redis \ && make -C /usr/src/redis install \ && rm -rf /var/lib/apt/lists/* \ && rm redis.tar.gz \ && rm -r /usr/src/redis \ && apt-get purge -y --auto-remove$buildDeps
所有的命令只有一个目的,就是编译、安装 redis 可执行文件。因此没有必要建立很多层,这只是一层的事情。因此,这里没有使用很多个 RUN 对应不同的命令,而是仅仅使用一个 RUN 指令,并使用 && 将各个所需命令串联起来。
一组命令的最后添加了清理工作的命令,删除了为了编译构建所需要的软件,清理了所有下载、展开的文件,并且还清理了 apt 缓存文件。这是很重要的一步
Dockerfile 支持 Shell 类的行尾添加 \ 的命令换行方式,以及行首 # 进行注释的格式
运行Dockerfile
$ docker build -t nginx:v3 . //假设该该目录下含有Dockerfile 注意后面有个点,后面会解释
指定了最终镜像的名称 -t nginx:v3
need-to-insert-img
生成的镜像
查看镜像运行历史
docker history nginx:v3
通过镜像运行容器
docker run --name web2 -d -p 81:80 nginx:v2
此时通过127.0.0.1:81访问即可访问到docker服务
点的解释(镜像构建上下文(Context))
原理
docker build的工作原理。Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。Docker 的引擎提供了一组 REST API,被称为Docker Remote API,而如docker命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种docker功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举。
使用场景
当我们进行镜像构建的时候,并非所有定制都会通过 RUN 指令完成,经常会需要将一些本地文件复制进镜像,比如通过 COPY 指令、ADD 指令等。而 docker build 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。在这种客户端/服务端的架构中,服务端应该如何获得本地文件,这就是这个上下文的使用场景。
使用方式
当构建的时候,用户会指定构建镜像上下文的路径,docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。
如果在 Dockerfile 中这么写:
> COPY ./package.json /app/
这并不是要复制执行 docker build 命令所在的目录下的 package.json,也不是复制 Dockerfile 所在目录下的 package.json,而是复制 上下文(context) 目录下的 package.json。
应用
刚才的命令 docker build -t nginx:v3 . 中的这个 .,实际上是在指定上下文的目录,docker build 命令会将该目录下的内容打包交给 Docker 引擎以帮助构建镜像。
Dockerfile命令
COPY
格式
COPY<源路径>...<目标路径>COPY ["<源路径1>",... "<目标路径>"]
COPY指令将从构建上下文目录中<源路径>的文件/目录复制到新的一层的镜像内的<目标路径>位置。比如:
COPYpackage.json /usr/src/app/
<源路径>可以是多个,甚至可以是通配符,其通配符规则要满足 Go 的filepath.Match规则,如:
COPY hom*/mydir/COPY hom?.txt /mydir/
<目标路径>可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用WORKDIR指令来指定)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。
ADD
可以自动解压缩 如果是链接也会自动下载,但是功能环境不同,各种功能不能完全预测,所以最佳实践会推荐
所有的文件复制均使用 COPY 指令,仅在需要自动解压缩的场合使用 ADD。
CMD 容器启动命令
Docker 不是虚拟机,容器就是进程。既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。CMD 指令就是用于指定默认的容器主进程的启动命令的。
在运行时可以指定新的命令来替代镜像设置中的这个默认命令,比如,ubuntu 镜像默认的 CMD 是 /bin/bash,如果我们直接 docker run -it ubuntu 的话,会直接进入 bash。我们也可以在运行时指定运行别的命令,如 docker run -it ubuntu cat /etc/os-release。这就是用 cat /etc/os-release 命令替换了默认的 /bin/bash 命令了,输出了系统版本信息。
ENTRYPOINT 入口点(RUN或者其他命令要与参数空一个空格)
ENTRYPOINT 的格式和 RUN 指令格式一样,分为 exec 格式和 shell 格式。
ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数。ENTRYPOINT 在运行时也可以替代,不过比 CMD 要略显繁琐,需要通过 docker run 的参数 --entrypoint 来指定。
当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令,换句话说实际执行时,将变为:
""
示例
FROM ubuntu:16.04RUN apt-get update \ && apt-get install -y curl \ && rm -rf /var/lib/apt/lists/*CMD ["curl","-s","http://ip.cn"]
$ docker run myip当前 IP:61.148.226.66来自:北京市 联通
嗯,这么看起来好像可以直接把镜像当做命令使用了,不过命令总有参数,如果我们希望加参数呢?比如从上面的 CMD 中可以看到实质的命令是 curl,那么如果我们希望显示 HTTP 头信息,就需要加上 -i 参数。那么我们可以直接加 -i 参数给 docker run myip 么?
$ docker run myip -idocker:Errorresponsefromdaemon: invalid header field value"oci runtime error: container_linux.go:247: starting container process caused \"exec: \\\"-i\\\": executable file not found in $PATH\"\n".
显然出了问题,但是entrypoint就不会有这样的问题
RUN apt-get update \ && apt-get install -y curl \ && rm -rf /var/lib/apt/lists/*ENTRYPOINT ["curl","-s","http://ip.cn"]
这次我们再来尝试直接使用
docker run myip -i:$ docker run myip当前 IP:61.148.226.66来自:北京市 联通$ docker run myip -iHTTP/1.1200OKServer: nginx/1.8.0Date: Tue,22Nov201605:12:40GMTContent-Type: text/html; charset=UTF-8Vary: Accept-EncodingX-Powered-By: PHP/5.6.24-1~dotdeb+7.1X-Cache: MISSfromcache-2X-Cache-Lookup: MISSfromcache-2:80X-Cache: MISSfromproxy-2_6Transfer-Encoding: chunkedVia:1.1cache-2:80,1.1proxy-2_6:8006Connection: keep-alive当前 IP:61.148.226.66来自:北京市 联通
没有问题,最后的-i被entrypoint当做参数加进去了。
ENV 设置环境变量
格式有两种:
ENVENV==...
这个指令很简单,就是设置环境变量而已,无论是后面的其它指令,如 RUN,还是运行时的应用,都可以直接使用这里定义的环境变量。对含有空格的值用双引号括起来的办法
ENV VERSION=1.0 DEBUG=on \ NAME="Happy Feet"
ARG 构建参数
格式:ARG <参数名>[=<默认值>]
构建参数和 ENV 的效果一样,都是设置环境变量。所不同的是,ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。但是不要因此就使用 ARG 保存密码之类的信息,因为 docker history 还是可以看到所有值的。
Dockerfile 中的 ARG 指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令 docker build 中用 --build-arg <参数名>=<值> 来覆盖。
VOLUME 定义匿名卷
格式为:
VOLUME ["<路径1>", "<路径2>"...]VOLUME<路径>
之前我们说过,容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中,后面的章节我们会进一步介绍 Docker 卷的概念。为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在 Dockerfile 中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。
VOLUME /data
这里的 /data 目录就会在运行时自动挂载为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化。当然,运行时可以覆盖这个挂载设置。比如:
docker run -d -v mydata:/data xxxx
在这行命令中,就使用了 mydata 这个命名卷挂载到了 /data 这个位置,替代了 Dockerfile 中定义的匿名卷的挂载配置。
EXPOSE 声明端口
格式为 EXPOSE <端口1> [<端口2>...]。
要将 EXPOSE 和在运行时使用 -p <宿主端口>:<容器端口> 区分开来。-p,是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问,而 EXPOSE 仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射。
WORKDIR 指定工作目录
格式为 WORKDIR <工作目录路径>。
使用 WORKDIR 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会帮你建立目录。
USER 指定当前用户
格式:USER <用户名>
USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层。WORKDIR 是改变工作目录,USER 则是改变之后层的执行 RUN, CMD 以及 ENTRYPOINT 这类命令的身份。
当然,和 WORKDIR 一样,USER 只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。
RUN groupadd -r redis && useradd -r -g redis redisUSER redisRUN ["redis-server"]
HEALTHCHECK 健康检查
FROM nginxRUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*HEALTHCHECK --interval=5s --timeout=3s \ CMD curl -fshttp://localhost/||exit1
这里我们设置了每 5 秒检查一次(这里为了试验所以间隔非常短,实际应该相对较长),如果健康检查命令超过 3 秒没响应就视为失败,并且使用
curl -fshttp://localhost/|| exit 1
作为健康检查命令。
作者:帅气的鱼先森
链接:https://www.jianshu.com/p/93e7a0cd3e2e
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
网友评论