首页
常用命令
About Me
推荐
weibo
github
Search
1
linuxea:gitlab-ci之docker镜像质量品质报告
48,760 阅读
2
linuxea:如何复现查看docker run参数命令
19,489 阅读
3
Graylog收集文件日志实例
17,808 阅读
4
git+jenkins发布和回滚示例
17,364 阅读
5
linuxea:jenkins+pipeline+gitlab+ansible快速安装配置(1)
17,353 阅读
ops
Openvpn
Sys Basics
rsync
Mail
NFS
Other
Network
HeartBeat
server 08
Code
Awk
Shell
Python
Golang
virtualization
KVM
Docker
openstack
Xen
kubernetes
kubernetes-cni
Service Mesh
Data
Mariadb
PostgreSQL
MongoDB
Redis
MQ
Ceph
TimescaleDB
kafka
surveillance system
zabbix
ELK Stack
Open-Falcon
Prometheus
Web
apache
Tomcat
Nginx
自动化
Puppet
Ansible
saltstack
Proxy
HAproxy
Lvs
varnish
更多
音乐
影视
music
Internet Consulting
最后的净土
软件交付
持续集成
gitops
devops
登录
Search
标签搜索
kubernetes
docker
zabbix
Golang
mariadb
持续集成工具
白话容器
linux基础
nginx
elk
dockerfile
Gitlab-ci/cd
最后的净土
基础命令
jenkins
docker-compose
gitops
haproxy
saltstack
Istio
marksugar
累计撰写
675
篇文章
累计收到
140
条评论
首页
栏目
ops
Openvpn
Sys Basics
rsync
Mail
NFS
Other
Network
HeartBeat
server 08
Code
Awk
Shell
Python
Golang
virtualization
KVM
Docker
openstack
Xen
kubernetes
kubernetes-cni
Service Mesh
Data
Mariadb
PostgreSQL
MongoDB
Redis
MQ
Ceph
TimescaleDB
kafka
surveillance system
zabbix
ELK Stack
Open-Falcon
Prometheus
Web
apache
Tomcat
Nginx
自动化
Puppet
Ansible
saltstack
Proxy
HAproxy
Lvs
varnish
更多
音乐
影视
music
Internet Consulting
最后的净土
软件交付
持续集成
gitops
devops
页面
常用命令
About Me
推荐
weibo
github
搜索到
26
篇与
白话容器
的结果
2019-01-21
linuxea:白话容器之Registry与Harbor的构建和使用 (26)
Registry用于保存docker镜像,包括镜像的层次结构和元数据,用户可以自己创建Registry,也可以使用官方的dockerhub,这些Registry支持第三方注册。但是这些Registry在互联网上,使用时候下载的速度不会再快,并且在实际使用中,有多数进行下载,宽带也会成为一个问题。为了达到最快的拉取目的,我们就需要自己构建Registry。Private RegistryRegistry主要用于托管镜像,Registry默认是https的,如果要使用http就需要特别的指定。为了能够使我们快速构建Registry,dockre提供了一个项目distribution,安装即可。而在docker官方已经将Registry作为镜像,由此,我们直接使用Registry镜像就可以运行Registry。Registry主要用于托管镜像,而Registry本身也是托管在容器中,容器的文件系统会随着容器的终止而删除的,Registry运行在容器后这些镜像是不能存放在容器中的,我们将它存储到存储卷内,如:网络存储。当然也可以在本地存放。这种方式很简单就可以运行起来一个Registry。如果不想使用这种方式,也可以使用docker-distribution安装docker-distribution[root@www-linuxea ~]$ yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo[root@www-linuxea ~]$ yum install docker-distribution -y[root@www-linuxea ~]$ rpm -ql docker-distribution /etc/docker-distribution/registry/config.yml 配置文件 /usr/bin/registry /usr/lib/systemd/system/docker-distribution.service /usr/share/doc/docker-distribution-2.6.2 /usr/share/doc/docker-distribution-2.6.2/AUTHORS /usr/share/doc/docker-distribution-2.6.2/CONTRIBUTING.md /usr/share/doc/docker-distribution-2.6.2/LICENSE /usr/share/doc/docker-distribution-2.6.2/MAINTAINERS /usr/share/doc/docker-distribution-2.6.2/README.md /var/lib/registry 仓库默认目录而后我修改了仓库目录[root@www-linuxea ~]$ mkdir /data/Registry[root@www-linuxea ~]$ cat /etc/docker-distribution/registry/config.yml version: 0.1 log: fields: service: registry storage: cache: layerinfo: inmemory filesystem: rootdirectory: /data/Registry http: addr: :5000[root@www-linuxea ~]$ systemctl start docker-distribution.service [root@www-linuxea ~]$ ss -tlnp|grep 5000 LISTEN 0 32768 *:5000 *:* users:(("registry",pid=9228,fd=3))如果5000端口正常就说明启动成功管理标签我修改了hosts,通过域名来进行打标[root@www-linuxea ~]$ echo "10.10.240.145 registry.linuxea.com" >> /etc/hosts[root@www-linuxea ~]$ cat /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 10.10.10.250 mirrors.ds.com 10.10.240.145 registry.linuxea.com而后重新打标签,将marksugar/httpd:v7 修改为registry.linuxea.com:5000/marksugar/httpd:v7[root@www-linuxea ~]$ docker tag marksugar/httpd:v7 registry.linuxea.com:5000/marksugar/httpd:v7推送默认是要使用https协议,而服务器断是http,所以这里会报错如下[root@www-linuxea ~]$ docker push registry.linuxea.com:5000/marksugar/httpd:v7 The push refers to repository [registry.linuxea.com:5000/marksugar/httpd] Get https://registry.linuxea.com:5000/v2/: http: server gave HTTP response to HTTPS client通过标记http来解决[root@www-linuxea ~]$ cat /etc/docker/daemon.json { "insecure-registries": ["registry.linuxea.com:5000"] }而后重启[root@www-linuxea ~]$ systemctl restart docker 而后再次推送[root@www-linuxea ~]$ docker push registry.linuxea.com:5000/marksugar/httpd:v7 The push refers to repository [registry.linuxea.com:5000/marksugar/httpd] e37e4dcca4e6: Pushed 7e0e76680fcf: Pushed 7e36f706b93d: Pushed 2d1436269532: Pushed 7030e9e24693: Pushed df64d3292fd6: Pushed v7: digest: sha256:9fd332615f424252c54583f31946816039a5f69d648a35034426be904c75a5e6 size: 1568推送的文件存放在存储目录下,我这里的路径位置是/data/Registry/docker/registry/v2/repositories/marksugar/httpd,在此之下是层目录和上传目录,这些文件又会链接到blobs下[root@DS-linuxea ~]$ ls /data/Registry/docker/registry/v2/repositories/marksugar/httpd _layers _manifests _uploads这些推送过的镜像如果要被pull使用,那么使用的客户端就也需要配置insecure-registriesHarborHarbor是vmware开源的镜像仓库,harbor的安装是非常麻烦的,不过可以使用compose来组合安装。而docker-compose也是一个简单的编排工具,这个编排工具中可以将docker run的命令组合在一个文件中调用,并且可以控制容器的启动顺序。docker-compose的前身是Fig,也是最早最火的编排工具,比k8s早,而后docker公司收购Fig。而在docker-compose中也有多个版本,有2,3版本我们下载离线包安装https://storage.googleapis.com/harbor-releases/release-1.6.0/harbor-offline-installer-v1.6.2.tgz[root@www-Node61_linuxea ~]$ tar xf harbor-offline-installer-v1.6.2.tgz -C /data [root@www-Node61_linuxea ~]$ cd /data/harbor/在harbor.cfg中需要按需修改一些配置,比如hostname,而后./install.sh 在这个docker-compose中定义的文件的映射关系,启动的服务顺序,网络模式,暴露端口等都被事先预设好了,也就是精心设计过的。暂停服务:docker-compose stop重启服务:docker-compose restart启动服务:docker-compose -f ./docker-compose.yaml up -d关闭删除服务:docker-compose down[root@www-Node61_linuxea /data/harbor]# ./install.sh ... [Step 4]: starting Harbor ... Creating network "harbor_harbor" with the default driver Creating harbor-log ... done Creating harbor-adminserver ... done Creating registry ... done Creating redis ... done Creating harbor-db ... done Creating harbor-ui ... done Creating harbor-jobservice ... done Creating nginx ... done ✔ ----Harbor has been installed and started successfully.---- Now you should be able to visit the admin portal at http://registry.linuxea.com. For more details, please visit https://github.com/goharbor/harbor . ...Harbor是监听在宿主机的80端口,我们通过浏览器直接访问。用户名:admin,密码:Harbor12345vmware Harbor的开发有一大部分是中国人,大概也就是如此才会这么快就有中文支持而后可以创建普通用户普通用户也是可以创建项目,推送镜像的客户端推送镜像到仓库新建一个dev项目,而后推送,格式如下推送前需要重新修改标签推送镜像 在项目中标记镜像: docker tag SOURCE_IMAGE[:TAG] registry.linuxea.com/dev/IMAGE[:TAG] 推送镜像到当前项目: docker push registry.linuxea.com/dev/IMAGE[:TAG]NODT那么,在推送之前,我们还需要将此前的配置修改,此前使用distribution,是5000端口,此刻使用Harbor的80端口[root@www-LinuxEA ~]$ cat /etc/docker/daemon.json { "insecure-registries": ["registry.linuxea.com"] }重启[root@www-LinuxEA /data/harbor]$ systemctl restart docker打标签[root@www-LinuxEA ~]$ docker tag marksugar/httpd:v7 registry.linuxea.com/dev/httpd:v7而后登陆到Harbor[root@www-LinuxEA ~]$ docker login registry.linuxea.com Username: linuxea Password: WARNING! Your password will be stored unencrypted in /root/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credentials-store Login Succeeded推送即可[root@www-LinuxEA ~]$ docker push registry.linuxea.com/dev/httpd:v7 The push refers to repository [registry.linuxea.com/dev/httpd] e37e4dcca4e6: Pushed 7e0e76680fcf: Pushed 7e36f706b93d: Pushed 2d1436269532: Pushed 7030e9e24693: Pushed df64d3292fd6: Pushed v7: digest: sha256:9fd332615f424252c54583f31946816039a5f69d648a35034426be904c75a5e6 size: 1568回到Harbor,已经能够看到这些详细的信息。
2019年01月21日
3,134 阅读
0 评论
0 点赞
2019-01-18
linuxea:白话容器之CPU与内存资源限制测试(25)
配置在前面了解了容器之CPU与内存资源限制概述,我们进行简单的资源限制测试我们下载lorel/docker-stress-ng测试我们在dockerhub上选择latest版本拉取到本地[root@linuxEA-145 /data/harbor]$ docker pull lorel/docker-stress-ng Using default tag: latest latest: Pulling from lorel/docker-stress-ng c52e3ed763ff: Pull complete a3ed95caeb02: Pull complete 7f831269c70e: Pull complete Digest: sha256:c8776b750869e274b340f8e8eb9a7d8fb2472edd5b25ff5b7d55728bca681322 Status: Downloaded newer image for lorel/docker-stress-ng:latestmemory可以使用 docker run --name stress --rm lorel/docker-stress-ng --help查看帮助 -m N, --vm N start N workers spinning on anonymous mmap (-m N,- vm N启动N个工作人员在匿名mmap上旋转) --vm-bytes N allocate N bytes per vm worker (default 256MB) (--vm-bytes N为每个vm worker分配N个字节(默认为256MB) )我们先对内存进行压测使用-m 指定配置大小为256m,而后使用 --vm 2 ,一个vm的默认是256M,压测观测下[root@linuxEA-145 /data/harbor]$ docker run --name stress --rm -m 256m lorel/docker-stress-ng --vm 2 stress-ng: info: [1] defaulting to a 86400 second run per stressor stress-ng: info: [1] dispatching hogs: 2 vm观测内存的使用情况[root@linuxEA-145 ~]$ docker top stress UID PID PPID C STIME TTY TIME CMD root 12685 12670 0 11:52 ? 00:00:00 /usr/bin/stress-ng --vm 2 root 12716 12685 0 11:52 ? 00:00:00 /usr/bin/stress-ng --vm 2 root 12717 12685 0 11:52 ? 00:00:00 /usr/bin/stress-ng --vm 2 root 12799 12717 70 11:52 ? 00:00:00 /usr/bin/stress-ng --vm 2 root 12810 12716 5 11:52 ? 00:00:00 /usr/bin/stress-ng --vm 2可以使用docker stats stress,内存最多使用在限制内的大小[root@linuxEA-145 ~]$ docker stats stress CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 3b96828e471a stress 0.00% 255.9MiB / 256MiB 99.95% 712B / 42B 2.36GB / 20.5GB 5 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 3b96828e471a stress 137.67% 255.8MiB / 256MiB 99.92% 712B / 42B 2.53GB / 21.8GB 5 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 3b96828e471a stress 137.67% 255.8MiB / 256MiB 99.92% 712B / 42B 2.53GB / 21.8GB 5 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 3b96828e471a stress 139.01% 255.8MiB / 256MiB 99.91% 712B / 42B 2.69GB / 23GB 5可以看到这个内存一直在使用达99%cpu我们在验证下CPU限制使用2核心,也就是最多使用200%,运行8个进程使用(无论起多少进程,只有2核心分配)请注意,这样的限制只能使用2个cpu,那就意味着0-3的cpu任何两个都会被使用,并不固定在那一颗CPU[root@linuxEA-145 /data/harbor]$ docker run --name stress --rm --cpus 2 lorel/docker-stress-ng --cpu 8 stress-ng: info: [1] defaulting to a 86400 second run per stressor stress-ng: info: [1] dispatching hogs: 8 cpu使用docker top stress观测,8个进程已经运行[root@linuxEA-145 ~]$ docker top stress UID PID PPID C STIME TTY TIME CMD root 32201 32185 0 14:19 ? 00:00:00 /usr/bin/stress-ng --cpu 8 root 32232 32201 25 14:19 ? 00:00:01 /usr/bin/stress-ng --cpu 8 root 32233 32201 25 14:19 ? 00:00:01 /usr/bin/stress-ng --cpu 8 root 32234 32201 25 14:19 ? 00:00:01 /usr/bin/stress-ng --cpu 8 root 32235 32201 25 14:19 ? 00:00:01 /usr/bin/stress-ng --cpu 8 root 32236 32201 25 14:19 ? 00:00:01 /usr/bin/stress-ng --cpu 8 root 32237 32201 25 14:19 ? 00:00:01 /usr/bin/stress-ng --cpu 8 root 32238 32201 25 14:19 ? 00:00:01 /usr/bin/stress-ng --cpu 8 root 32239 32201 25 14:19 ? 00:00:01 /usr/bin/stress-ng --cpu 8而CPU最多使用200[root@linuxEA-145 ~]$ docker stats stress CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS d4b2a1641d67 stress 200.05% 18.77MiB / 3.848GiB 0.48% 0B / 42B 0B / 0B 9 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS d4b2a1641d67 stress 200.05% 18.77MiB / 3.848GiB 0.48% 0B / 42B 0B / 0B 9 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS d4b2a1641d67 stress 199.37% 18.77MiB / 3.848GiB 0.48% 0B / 42B 0B / 0B 9 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS d4b2a1641d67 stress 199.37% 18.77MiB / 3.848GiB 0.48% 0B / 42B 0B / 0B 9 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS d4b2a1641d67 stress 200.50% 18.77MiB / 3.848GiB 0.48% 0B / 42B 0B / 0B 9 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS d4b2a1641d67 stress 200.50% 18.77MiB / 3.848GiB 0.48% 0B / 42B 0B / 0B 9 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS d4b2a1641d67 stress 200.10% 18.77MiB / 3.848GiB 0.48% 0B / 42B 0B / 0B 9 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS假如我们没有限制,将会跑近400%,也就是全部资源被使用当然,也可以指定运行在那一颗之上。比如,限制运行在0,2上这样--cpuset-cpus 0,2 只会运行在0,2不会运行在其他的CPU核心上。[root@linuxEA-145 /data/harbor]$ docker run --name stress --rm --cpuset-cpus 0,2 lorel/docker-stress-ng --cpu 8 stress-ng: info: [1] defaulting to a 86400 second run per stressor stress-ng: info: [1] dispatching hogs: 8 cpu仍然在限制内[root@linuxEA-145 ~]$ docker stats stress CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 21537336658b stress 200.76% 21.71MiB / 3.848GiB 0.55% 0B / 42B 0B / 0B 9 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 21537336658b stress 200.76% 21.71MiB / 3.848GiB 0.55% 0B / 42B 0B / 0B 9 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 21537336658b stress 200.29% 21.71MiB / 3.848GiB 0.55% 0B / 42B 0B / 0B 9 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 21537336658b stress 200.29% 21.71MiB / 3.848GiB 0.55% 0B / 42B 0B / 0B 9 而后我们在宿主机查看top,0,2cpu已经跑满,说明限制是ok的[root@linuxEA-145 /data/harbor]$ top top - 14:25:02 up 85 days, 23:51, 4 users, load average: 2.37, 3.31, 3.04 Tasks: 167 total, 9 running, 91 sleeping, 0 stopped, 0 zombie %Cpu0 : 99.3/0.3 100[||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ] %Cpu1 : 0.7/0.7 1[|| ] %Cpu2 : 100.0/0.0 100[||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||] %Cpu3 : 1.0/1.0 2[|| ] KiB Mem : 29.4/4034596 [||||||||||||||||||||||||||||| ] KiB Swap: 1.9/4190204 [|| ] PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 582 root 20 0 6924 2444 620 R 25.2 0.1 0:04.17 stress-ng-cpu 590 root 20 0 6924 2444 620 R 25.2 0.1 0:04.16 stress-ng-cpu 583 root 20 0 6924 3960 620 R 24.9 0.1 0:04.16 stress-ng-cpu 584 root 20 0 6924 3960 620 R 24.9 0.1 0:04.16 stress-ng-cpu 585 root 20 0 6924 2444 620 R 24.9 0.1 0:04.16 stress-ng-cpu 587 root 20 0 6924 2444 620 R 24.9 0.1 0:04.16 stress-ng-cpu --cpu-shares共享那如果是共享的方式就不同了共享的方式--cpu-shares,在没有使用的时候都会被吃掉,如果有其他的容器运行则分配给别人[root@linuxEA-145 /data/harbor]$ docker run --name stress --rm --cpu-shares 1024 lorel/docker-stress-ng stress --cpu 8 stress-ng: info: [1] defaulting to a 86400 second run per stressor stress-ng: info: [1] dispatching hogs: 8 cpu只运行一个容器的时候,CPU 400%[root@linuxEA-145 ~]$ docker stats stress CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS dc3c61506fb9 stress 399.67% 17.14MiB / 3.848GiB 0.44% 0B / 42B 0B / 0B 9 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS dc3c61506fb9 stress 399.67% 17.14MiB / 3.848GiB 0.44% 0B / 42B 0B / 0B 9 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS dc3c61506fb9 stress 400.42% 17.14MiB / 3.848GiB 0.44% 0B / 42B 0B / 0B 9 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS dc3c61506fb9 stress 400.42% 17.14MiB / 3.848GiB 0.44% 0B / 42B 0B / 0B 9我们在运行一个容器,也做限制为512[root@linuxEA-145 /data/harbor]$ docker run --name stress1 --rm --cpu-shares 512 lorel/docker-stress-ng stress --cpu 8 stress-ng: info: [1] defaulting to a 86400 second run per stressor stress-ng: info: [1] dispatching hogs: 8 cpu在来观测此前运行一个容器时候的stress,现在已经掉到266%左右[root@linuxEA-145 ~]$ docker stats stress CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS dc3c61506fb9 stress 265.84% 17.14MiB / 3.848GiB 0.44% 1.07kB / 42B 0B / 0B 9 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS dc3c61506fb9 stress 267.23% 17.14MiB / 3.848GiB 0.44% 1.07kB / 42B 0B / 0B 9 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS dc3c61506fb9 stress 267.23% 17.14MiB / 3.848GiB 0.44% 1.07kB / 42B 0B / 0B 9 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS dc3c61506fb9 stress 265.52% 17.14MiB / 3.848GiB 0.44% 1.07kB / 42B 0B / 0B 而stress1的使用是133%[root@linuxEA-145 ~]$ docker stats stress1 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 2db9d935886c stress1 133.23% 17.16MiB / 3.848GiB 0.44% 1.07kB / 42B 0B / 0B 9 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 2db9d935886c stress1 132.16% 17.16MiB / 3.848GiB 0.44% 1.07kB / 42B 0B / 0B 9 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 2db9d935886c stress1 132.16% 17.16MiB / 3.848GiB 0.44% 1.07kB / 42B 0B / 0B 9 CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 2db9d935886c stress1 133.08% 17.16MiB / 3.848GiB 0.44% 1.07kB / 42B 0B / 0B CPU资源被两个容器动态瓜分在资源紧张的情况下,可以调整--oom-kill-disadble禁用被kill,和--oom-score-adj调整优先级来尽量避免优先被kill
2019年01月18日
3,211 阅读
0 评论
0 点赞
2019-01-17
linuxea:白话容器之CPU与内存资源限制概述(24)
我们知道容器能够运行,依赖内核的两个特性,一个是名称空间,一个是控制组。默认在docker中是没有任何资源限制的,在某些极端情况下能够几乎耗尽docker主机之上的所有资源。在此基础之上docker provides提供了一个控制容器能够使用多少内存,cpu,io,我们可以进行控制cpu和内存,而IO较弱控制。而其中的内存是非可压缩资源,CPU是可压缩资源。当容器内的进程耗尽CPU时,并且申请更多内存时候,可能会产生OOM。而CPU则不会出现这种问题。这些资源的限制依赖于capabilities资源限制从名称空间和cgroup来进行定义MemoryOOM如果在linux主机上,内核探测到当前宿主机已经没有足够内存可用于执行某些重要的系统功能,就会抛出OOME,并且开始启动kill 进程,以便于释放资源。一旦发生OOME,任何进程都有可能会被杀死 ,包括docker daemon在内如果内核发现内存被几乎耗尽无法执行本身的运作的时候,会进行检测使用内存异常的hogs进程。但是并不是使用内存最多的就是被杀掉的进程。这可能取决于,申请的内存总大小内耗尽比例来取决最佳被杀掉的进程,这个列表顺序可能是逆序的。为此,docker特地调整了docker daemon的OOM优先级,但是容器的优先级并未调整。为了便于控制,引入了一个oom-adj的功能,以防止系统中的重要进程被杀死,并定义要杀死的进程的顺序。oom_adj的可能值范围为-17到+15。分数越高,相关过程越有可能被OOM杀手杀死。如果 oom_adj设置为-17,则不会考虑将该进程用于OOM查杀。在每一个进程可以分配一个OOM-score的得分计算,根据其不良分数选择在内存不足情况下被杀死,,这个得分计算根据内存的申请的空间参考:https://lwn.net/Articles/317814/ https://lwn.net/Articles/391222/ https://lwn.net/Articles/548180/ https://lwn.net/Articles/391206/对于一些非常重要的容器,在启动的时候就需要调整oom_adj,另外也可以定义容器的策略,在出现OOM被kill后就restart默认是没有对资源限制的,限制容器的内存和CPU资源是有选项可以使用:内存资源raw,swap| `-m` 要么 `--memory=` | 限制容器使用内存,单位是k,b,m,g作为单位使用,可单独使用 | | `--memory-swap`* | 允许此容器交换到磁盘的内存量。查看[`--memory-swap`详情](https://docs.docker.com/config/containers/resource_constraints/#--memory-swap-details)。这个选项生效依赖于raw的选项是否配置 | | `--memory-swappiness` | 默认情况下,主机内核可以交换容器使用的匿名页面的百分比。您可以设置`--memory-swappiness`0到100之间的值,以调整此百分比。查看[`--memory-swappiness`详情](https://docs.docker.com/config/containers/resource_constraints/#--memory-swappiness-details)。 0并不是不使用| | `--memory-reservation` | 允许您指定小于软件限制的软限制`--memory`,当Docker检测到主机上的争用或内存不足时,该限制将被激活。如果使用`--memory-reservation`,则必须将其设置为低于`--memory`优先级。因为它是软限制,所以不保证容器不超过限制。 | | `--kernel-memory` | 容器可以使用的最大内核内存量。允许的最小值是`4m`。由于内核内存无法换出,因此内核内存不足的容器可能会阻塞主机资源,这可能会对主机和其他容器产生副作用。查看[`--kernel-memory`详情](https://docs.docker.com/config/containers/resource_constraints/#--kernel-memory-details)。 | | `--oom-kill-disable` | 默认情况下,如果发生内存不足(OOM)错误,内核会终止容器中的进程。要更改此行为,请使用该`--oom-kill-disable`选项。仅在已设置`-m/--memory`选项的容器上禁用OOM杀手。如果`-m`未设置该标志,则主机可能会耗尽内存,并且内核可能需要终止主机系统的进程才能释放内存。 |参考:https://docs.docker.com/config/containers/resource_constraints/#limit-a-containers-access-to-memory--memory-swap和memory联系与区别memswapmemory功能正数s正数M容器可用中总空间为s,其中ram为M,swap为(s-m),若S=M,则无可用swap资源。这里的可用内存并不是s+m!0正数M下相当于未设置swap不设置正数M若主机(docker host)启用了swap,则容器的可用swap为2*M-1正数M若主机(docker host)启用了swap,则容器可使用最大至宿主机上的所有swap空间的swap资源容器内使用free命令看到的swap空间并不具有其所展现出的空间的指示意义CPU每一个容器都可以使用宿主机上所有的CPU资源。作为用户来讲,可以设置各种约束来限制给定容器访问主机的CPU额度。我们知道,CPU的核心数是少于进程数的,一个系统之上运行了很多个进程,运行的进程数量大于核心数量的时候, 那个才是优先被运行?大多数用户使用和配置是由CFS调度程序来决定的。内核管理中最重要的一个组件CFS(Completely Fair Scheduler),处理调度进程在本地的核心之上,用于普通Linux进程的Linux内核CPU调度程序。多个运行时标志允许您配置容器具有的CPU资源访问量。使用这些设置时,Docker会修改主机上容器的cgroup的设置。然而每个进程都是有优先级的,这种非实时优先级,有效范围是100-139,这个范围是可以使用nice(niceness scale)进行调整,-20到19,数字越小优先级越高。默认nice值为零。在1.13及更高版本中,支持实时调度程序。另外,还有实时优先级,0-99,默认范围为1到99,这些都是内核级的。然后100到139用于用户空间(非实时),调度器CFS就是调度这些100-139之间的进程的延伸阅读:CFS: https://www.kernel.org/doc/Documentation/scheduler/sched-design-CFS.txtCFS: https://en.wikipedia.org/wiki/Completely_Fair_Scheduler进程调度:https://en.wikipedia.org/wiki/Completely_Fair_SchedulerCPU调度:https://www.studytonight.com/operating-system/cpu-scheduling进程优先级调整:https://bencane.com/2013/09/09/setting-process-cpu-priority-with-nice-and-renice/https://developer.ibm.com/tutorials/l-lpic1-103-6/https://www.ostechnix.com/change-priority-process-linux/https://www.nixtutor.com/linux/changing-priority-on-linux-processes/https://www.tecmint.com/set-linux-process-priority-using-nice-and-renice-commands/CPU资源也分为io密集型和CPU密集型,对于cpu密集型的可能需要调低优先级,当调度后就会耗时较长,而IO密集型是IO调度。对于占用很长时间的进程动态调低优先级,对于那些占用CPU很少又经常调度不上去的(CPU)将会调整高一些。CPU分配:选项描述--cpus=<value>指定容器可以使用的可用CPU资源量。例如,如果主机有两个CPU并且您已设置--cpus="1.5",则容器最多保证一个半CPU。这相当于设置--cpu-period="100000"和--cpu-quota="150000"。可在Docker 1.13及更高版本中使用。--cpu-period=<value>指定CPU CFS调度程序周期,它与并用 --cpu-quota。默认为100微秒。大多数用户不会更改默认设置。如果您使用Docker 1.13或更高版本,请--cpus改用。--cpu-quota=<value>对容器施加CPU CFS配额。--cpu-period限制前容器限制为每秒的微秒数。作为有效上限。如果您使用Docker 1.13或更高版本,请--cpus改用。--cpuset-cpus限制容器可以使用的特定CPU或核心。如果您有多个CPU,则容器可以使用的以逗号分隔的列表或连字符分隔的CPU范围。第一个CPU编号为0.有效值可能是0-3(使用第一个,第二个,第三个和第四个CPU)或1,3(使用第二个和第四个CPU)。--cpu-shares将此标志设置为大于或小于默认值1024的值,以增加或减少容器的重量,并使其可以访问主机的CPU周期的较大或较小比例。仅在CPU周期受限时才会强制执行此操作。当有足够的CPU周期时,所有容器都会根据需要使用尽可能多的CPU。这样,这是一个软限制。--cpu-shares不会阻止容器以群集模式进行调度。它为可用的CPU周期优先考虑容器CPU资源。它不保证或保留任何特定的CPU访问权限。参考:https://docs.docker.com/config/containers/resource_constraints/#configure-the-default-cfs-scheduler而--cpu-shares是按照比例切分。1024:512比如当前系统上运行了两个人容器,第一个容器是1024,第二个容器是512,假如这俩个容器都需要可能多的使用CPU。假如CPU资源有100份(100%),分为2分之一,各占512,而CPU资源以512为单位分为三份在两个容器上,如下:第一个容器占2份,而第二个容器占用1份,如果此时第二个容器启动后并没有占用CPU,那么第一个容器就可以占用第二个容器的512,这种方式就是共享式。在需要的时候就分配需要的额度,不需要的时候就给需要的使用。1024:512:2048假如现在有更大的比例,如下图这样一来就是分为了七份,2+1+4,分七份后后可用资源占用比例就不一样了,最后的2048是分成了4份542。同样,如果其中某一份容器或多个容器暂时不占用分配自己的CPU资源,仍然会被其他繁忙的容器所占用全部。也就是说,只要容器都在使用,就按照比例分配,如果只有一个使用,其他的不使用,这个使用的容器能够占用所有的CPU资源。如果有两个在使用,就按照剩下的比例分配给使用的,这个过程是随时动态调整的。任何一个容器不使用,就会被其他使用的容器说分配掉。CPU是可压缩资源,可随时加入更多的容器使用,只不过不同的分法,得到的结果不一样而已。CPU可压缩资源通过共享方式来分派,可压缩,也可随时调整比例。CPUshares有一特点,假如有4核心cpu,其中没一个容器的进程可任意调度到任何一个核心上面运行。一共使用1024比例,但是另外2个容器都不运行,那这个容器就不单单有一颗CPU的运算能力,而是有4颗CPU的运算能力,一颗CPU是100%,4个就相当于有400%的CPU。--cpus当然,也可以限制,假设如果一共有4核心,一个进程最多使用2个核心,且无论其他CPU空闲超过2个以上或者其他,被限制的cpu只能使用2核心,另外两个核心是空闲的(任意取其二,剩下不取) 。如下图左假设使用限制是2核心,也就是说,在4核心的cpu上使用的最多只能是200%,一个核心为100%,那也就是2颗核心。而现在可能是另外的一种方式,同样适用2核心,最多使用200%,但是却是每个核心各50%,重量不超过200%即可。如下图右但是,我们也可以限制核心数。--cpuset-cpus--cpus意味着这个进程只能运行在那个CPU核心。假如有4个核心,用的是0和3进行编号,那1和2就不能使用这些分配可以分为几种:1,按照压缩方式比例分配2,限定最多使用的核心数3,限定使用那些个CPU假如只有--cpuset-cpus限制了只能使用的0和1,那说明只能使用两个CPU,也就是200%,也可以限制使用的cpu时间周期和配额,这些选项在run或create时间使用。
2019年01月17日
3,695 阅读
0 评论
0 点赞
2019-01-16
linuxea:白话容器之dockerfile ARG和ONBUILD使用(6)(23)
shell该SHELL指令允许覆盖用于shell形式的命令的默认shell 。Linux上的默认shell是["/bin/sh", "-c"],而在Windows上["cmd", "/S", "/C"]。该SHELL指令必须以JSON格式写入Dockerfile。参考:https://docs.docker.com/engine/reference/builder/#shellSTOPSIGNAL该STOPSIGNAL指令设置将发送到容器的系统调用信号以退出。此信号可以是与内核的系统调用表中的位置匹配的有效无符号数,例如9,或SIGNAME格式的信号名,例如SIGKILL,例如15,停止容器。如果要发送其他的信号就要在这里定义STOPSIGNAL signalARGARG指令定义了一个变量,可以docker build使用该--build-arg <varname>=<value> 标志在构建时将该变量传递给构建器。这样定义的Dockerfile可以适用于较多的场景,尤其是程序版本迭代。如果指定了未在Dockerfile中定义的构建参数,则构建会输出警告。示例:定义ARG author默认是"linuxea.com.cn"ARG author="linuxea.com.cn" LABEL maintainer="${author}"[root@linuxEA-145 /data/linuxea3]$ cat Dockerfile FROM nginx:1.14.2-alpine ARG author="linuxea.com.cn" LABEL maintainer="${author}" ENV NGINX_ROOT="/data/wwwroot" ADD entrypoint.sh /bin/entrypoint.sh ADD index.html ${NGINX_ROOT}/ EXPOSE 8080/tcp 80/tcp HEALTHCHECK --interval=3s --timeout=3s --start-period=3s CMD wget -O - -q http://${IP:-0.0.0.0}:${NGPORT:-80}||exit 1 #HEALTHCHECK --start-period=3s CMD wget -O - -q http://${IP:-0.0.0.0}:${NGPORT} || exit 1 CMD ["/usr/sbin/nginx","-g","daemon off;"] ENTRYPOINT ["/bin/entrypoint.sh"]build[root@linuxEA-145 /data/linuxea3]$ docker build --build-arg author="mark www.linuxea.com" -t marksugar/nginx:v5 .查看,marksugar/nginx:v5的Labels是默认的linuxea.com.cn[root@linuxEA-145 /data/linuxea3]$ docker inspect -f {{.ContainerConfig.Labels}} marksugar/nginx:v5 map[maintainer:linuxea.com.cn]而后重新build,重新赋值--build-arg author="mark www.linuxea.com"[root@linuxEA-145 /data/linuxea3]$ docker build --build-arg author="mark www.linuxea.com" -t marksugar/nginx:v6 .而后在查看,赋值被生效[root@linuxEA-145 /data/linuxea3]$ docker inspect -f {{.ContainerConfig.Labels}} marksugar/nginx:v6 map[maintainer:mark www.linuxea.com]ONBUILDdocker onbuild用于指定当前的docker镜象用作另一个镜象的基本镜象时候运行的命令,在使用前,需要一个静态的基本镜象,而后其动态配置会在新的镜象(子镜象)发生作用,或者在新镜象依赖之前构建镜象的情况下使用!ONBUILD用于在Dockerfile中定义一个触发器,Dockerfile用于build镜像文件,此镜像文件可作为base image被另一个Dockerfile用作FROM指令的参数,并以之构建新的映像文件疑问当使用各种指令创建一个dockerfile,并在dockerfile指定了onbuild指令,当我们使用docker build使用当前的dockerfile构建镜象的时候,将会创建一个新的docker镜象,但onbuild指令不会应用于当前的docker镜象,仅仅作为将要创建新的景象用作为另外一个dockerfile中的基本镜象时候,才会应用此选项。在后面的这个Dockerfile中的FROM指令在build过程中被执行时,将会“触发”创建其base image的Dockerfile文件中的ONBUILD指令定义的触发器ONBULD <INSTRUCTION>尽管任何指令都可注册成为触发器指令,但ONBUILD不能自我嵌套,且不会触发FROM和MAINTAINER指令使用包含ONBUILD指令的Dockerfile构建的镜像应该使用特殊的标签,例如:mysql:5.6-onbuild在ONBUILD指令中使用ADD或COPY指令如果缺少指定的源文件时会失败(比如二次build copy,本地却无文件)。多数情况下,ONBUILD会执行ADD或者RUN进行下载安装等。也就说ONBUILD不会在自己构建的时候执行,而是在被其他人使用作为基础镜像的时候才会执行。添加一条ONBUILD,示例如下:ONBUILD ADD http://10.10.240.145/CentOS-Base.repo /etc/如下:[root@linuxEA-145 /data/linuxea3]$ cat Dockerfile FROM nginx:1.14.2-alpine ARG author="linuxea.com.cn" LABEL maintainer="${author}" ENV NGINX_ROOT="/data/wwwroot" ADD entrypoint.sh /bin/entrypoint.sh ADD index.html ${NGINX_ROOT}/ EXPOSE 8080/tcp 80/tcp HEALTHCHECK --interval=3s --timeout=3s --start-period=3s CMD wget -O - -q http://${IP:-0.0.0.0}:${NGPORT:-80}||exit 1 #HEALTHCHECK --start-period=3s CMD wget -O - -q http://${IP:-0.0.0.0}:${NGPORT} || exit 1 ONBUILD ADD http://10.10.240.145/CentOS-Base.repo /etc/ CMD ["/usr/sbin/nginx","-g","daemon off;"] ENTRYPOINT ["/bin/entrypoint.sh"]而后build为marksugar/httpd:v7[root@linuxEA-145 /data/linuxea3]$ docker build -t marksugar/httpd:v7 . Sending build context to Docker daemon 4.608kB Step 1/11 : FROM nginx:1.14.2-alpine ---> d956af1ad36a Step 2/11 : ARG author="linuxea.com.cn" ---> Using cache ---> c4c543206ad1 Step 3/11 : LABEL maintainer="${author}" ---> Using cache ---> 36edfcb86dfb Step 4/11 : ENV NGINX_ROOT="/data/wwwroot" ---> Using cache ---> 6547ed95f45a Step 5/11 : ADD entrypoint.sh /bin/entrypoint.sh ---> Using cache ---> d650f4dcb46c Step 6/11 : ADD index.html ${NGINX_ROOT}/ ---> Using cache ---> 865b4df58910 Step 7/11 : EXPOSE 8080/tcp 80/tcp ---> Using cache ---> 1454ea1fc5e3 Step 8/11 : HEALTHCHECK --interval=3s --timeout=3s --start-period=3s CMD wget -O - -q http://${IP:-0.0.0.0}:${NGPORT:-80}||exit 1 ---> Using cache ---> 157834157357 Step 9/11 : ONBUILD ADD http://10.10.240.145/CentOS-Base.repo /etc/ ---> Running in 37e29f61d449 Removing intermediate container 37e29f61d449 ---> f334451b2c4c Step 10/11 : CMD ["/usr/sbin/nginx","-g","daemon off;"] ---> Running in d3b24f896853 Removing intermediate container d3b24f896853 ---> b9307347b1ce Step 11/11 : ENTRYPOINT ["/bin/entrypoint.sh"] ---> Running in cd1f126e961f Removing intermediate container cd1f126e961f ---> a79987f1e18a Successfully built a79987f1e18a Successfully tagged marksugar/httpd:v7[root@linuxEA-145 /data/linuxea3]$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE marksugar/httpd v7 a79987f1e18a About a minute ago 17.7MB重新写一个Dockerfile,调用刚刚构建的marksugar/httpd:v7[root@linuxEA-145 /data/linuxea4]$ cat Dockerfile FROM marksugar/httpd:v7 RUN echo "helo" >> /tmp/linuxea.txt在build的时候,就会执行上一个Dockerfile中的ONBUILD命令,如下:[root@linuxEA-145 /data/linuxea4]$ docker build -t onbuild:1 . Sending build context to Docker daemon 2.048kB Step 1/2 : FROM marksugar/httpd:v7 # Executing 1 build trigger Downloading [==================================================>] 1.664kB/1.664kB ---> b64f3cd2ba7f Step 2/2 : RUN echo "helo" >> /tmp/linuxea.txt ---> Running in e9f45b68bb24 Removing intermediate container e9f45b68bb24 ---> 63bced5b493c Successfully built 63bced5b493c Successfully tagged onbuild:1而后run起来验证下[root@linuxEA-145 /data/linuxea4]$ docker run --name onbuild --rm onbuild:1 ls /etc/CentOS-Base.repo /etc/CentOS-Base.repo[root@linuxEA-145 /data/linuxea4]$ docker run --name onbuild --rm onbuild:1 cat /etc/CentOS-Base.repo # CentOS-Base.repo # # The mirror system uses the connecting IP address of the client and the # update status of each mirror to pick mirrors that are updated to and # geographically close to the client. You should use this for CentOS updates # unless you are manually picking other mirrors. # # If the mirrorlist= does not work for you, as a fall back you can try the # remarked out baseurl= line instead. # # [base] name=CentOS-$releasever - Base mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os&infra=$infra #baseurl=http://mirror.centos.org/centos/$releasever/os/$basearch/ gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 ...
2019年01月16日
3,783 阅读
0 评论
0 点赞
2019-01-14
linuxea:白话容器之dockerfile health check使用(5)(22)
health check当我们基于镜像启动一个容器的时候,容器在什么时候退出,又在什么时候运行?事实上只要这个容器的主进程不是运行在后台,也没有停止,那么这个容器就不会停止。容器在判断容器正常与否,并不会根据容器是否能够正常服务,而仅仅是看容器主进程是否运行着,因此这种判断机制并不能说明容器就是健康的。那么我们就可以使用curl,或者wget,向主页发起请求,如果主页内容状态是对的,或者内容是我们期望的来判断是否运行正常,这样的精度才是我们想要的health check并不是检测一次就是ok的,他是一个周期的持续性的检测当容器指定了运行状况检查时,除了正常状态外,它还具有运行状况。这个状态最初是starting。每当健康检查通过时,它就会变成healthy(以前的状态)。经过一定数量的连续失败后,它就变成了unhealthy--interval=DURATION(间隔时间,默认值:30s)--timeout=DURATION(超时秒数,默认值:30s)--start-period=DURATION(默认值:0s)--retries=N(失败的重试次数,默认值:3)start period:容器在启动的时候,可能需要一定的准备的初始化时间,如果容器被run就开始检测,那如果容器也没有准备妥当,那此时检测肯定是失败的。那么就需要给容器一个启动的准备时间来做初始化。而这个start-period就是。检测发出后返回状态值:0:成功 - 容器健康且随时可用1:不健康 - 容器无法正常工作2:保留 - 不要使用此退出代码例如,要检查每五分钟左右网络服务器能够在三秒钟内为网站的主页面提供服务:HEALTHCHECK --interval=5m --timeout=3s \ CMD curl -f http://localhost/ || exit 1这样以来就不依赖进程是否运行来判断,而是判断访问是否正常来判断接着上面的Dockerfile,添加一行,时间均是3s,方便测试:--interval=3s--timeout=3s--start-period=3sHEALTHCHECK --interval=3s --timeout=3s --start-period=3s CMD wget -O - -q http://${IP:-0.0.0.0}:${NGPORT:-80}[root@linuxEA-145 /data/linuxea3]$ cat Dockerfile FROM nginx:1.14.2-alpine LABEL maintainer="linuxea.com" ENV NGINX_ROOT="/data/wwwroot" ADD entrypoint.sh /bin/entrypoint.sh ADD index.html ${NGINX_ROOT}/ EXPOSE 8080/tcp 80/tcp HEALTHCHECK --interval=3s --timeout=3s --start-period=3s CMD wget -O - -q http://${IP:-0.0.0.0}:${NGPORT:-80} || exit 1 #HEALTHCHECK --start-period=3s CMD wget -O - -q http://${IP:-0.0.0.0}:${NGPORT} || exit 1 CMD ["/usr/sbin/nginx","-g","daemon off;"] ENTRYPOINT ["/bin/entrypoint.sh"]而后build[root@linuxEA /data/linuxea3]$ docker build -t marksugar/nginx:v3 .run起来后,就会发起wget -O - -q http://${IP:-0.0.0.0}:${NGPORT:-80},说明状态健康[root@linuxEA-145 /data/linuxea3]$ docker run --name linuxea --rm -e "NGPORT=8080" marksugar/nginx:v3 127.0.0.1 - - [08/Dec/2018:15:23:15 +0000] "GET / HTTP/1.1" 200 28 "-" "Wget" "-" 127.0.0.1 - - [08/Dec/2018:15:23:18 +0000] "GET / HTTP/1.1" 200 28 "-" "Wget" "-" 127.0.0.1 - - [08/Dec/2018:15:23:21 +0000] "GET / HTTP/1.1" 200 28 "-" "Wget" "-" 127.0.0.1 - - [08/Dec/2018:15:23:24 +0000] "GET / HTTP/1.1" 200 28 "-" "Wget" "-" 127.0.0.1 - - [08/Dec/2018:15:23:27 +0000] "GET / HTTP/1.1" 200 28 "-" "Wget" "-" 127.0.0.1 - - [08/Dec/2018:15:23:30 +0000] "GET / HTTP/1.1" 200 28 "-" "Wget" "-" 127.0.0.1 - - [08/Dec/2018:15:23:33 +0000] "GET / HTTP/1.1" 200 28 "-" "Wget" "-" 127.0.0.1 - - [08/Dec/2018:15:23:36 +0000] "GET / HTTP/1.1" 200 28 "-" "Wget" "-"此时我将端口修改为81,而81并没有监听,wget将失败,也就没有了wget日志,而docker ps 将能看到状态为unhealthy,说明不健康[root@linuxEA-145 /data/linuxea3]$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 13fcf1bd14c4 marksugar/nginx:v4 "/bin/entrypoint.sh …" 19 seconds ago Up 18 seconds (unhealthy) 80/tcp, 8080/tcp linuxea查看docker inspect --format "{{json .State.Health }}" <container name> | jq示例2我创建了一个小的node.js web服务器,只需用'OK'响应任何请求。但是,服务器还有一个切换开/关状态的开关,而不会实际关闭服务器的进程。这是它的代码:"use strict"; const http = require('http'); function createServer () { return http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('OK\n'); }).listen(8080); } let server = createServer(); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); if (server) { server.close(); server = null; res.end('Shutting down...\n'); } else { server = createServer(); res.end('Starting up...\n'); } }).listen(8081);当服务器处于开启状态时,它将在端口8080处侦听并返回OK到任何进入该端口的请求。curl 8081端口将关闭服务器,另一个curl将再次启用它:[root@linuxEA-145 ~]$ node server.js # switch to another terminal [root@linuxEA-145 ~]$ curl 127.0.0.1:8080 # OK [root@linuxEA-145 ~]$ curl 127.0.0.1:8081 # Shutting down... [root@linuxEA-145 ~]$ curl 127.0.0.1:8080 # curl: (7) Failed to connect to 127.0.0.1 port 8080: Connection refused [root@linuxEA-145 ~]$ curl 127.0.0.1:8081 # Starting up... [root@linuxEA-145 ~]$ curl 127.0.0.1:8080 # OK现在让我们将server.js放入带有运行状况检查的Dockerfile中,构建一个镜像并将其作为容器启动:FROM node COPY server.js / EXPOSE 8080 8081 HEALTHCHECK --interval=5s --timeout=10s --retries=3 CMD curl -sS 127.0.0.1:8080 || exit 1 CMD [ "node", "/server.js" ][root@linuxEA-145 ~]$ docker build . -t server:latest # Lots, lots of output [root@linuxEA-145 ~]$ docker run -d --rm -p 8080:8080 -p 8081:8081 server # ec36579aa452bf683cb17ee44cbab663d148f327be369821ec1df81b7a0e104b [root@linuxEA-145 ~]$ curl 127.0.0.1:8080 # OK创建的容器ID开头ec3,这应该足以在以后识别它,所以现在我们可以跳转到健康检查监视容器运行状况Docker用于检查容器健康状况的主要命令是 docker inspect。它产生巨大的JSON作为响应,但我们感兴趣的唯一部分是它的 State.Health属性:[root@linuxEA-145 ~]$ docker inspect ec3 | jq '.[].State.Health' #{ # "Status": "healthy", # "FailingStreak": 0, # "Log": [ # { # "Start": "2017-06-27T04:07:03.975506353Z", # "End": "2017-06-27T04:07:04.070844091Z", # "ExitCode": 0, # "Output": "OK\n" # }, #... }当前状态是“健康的”,我们甚至可以看到健康检查记录 Log 收集。但是,在调用端口8081并等待3 * 5秒(允许三次检查失败)之后,镜像将会改变。[root@linuxEA-145 ~]$ curl 127.0.0.1:8081 # Shutting down... # 15 seconds later [root@linuxEA-145 ~]$ docker inspect ec3 | jq '.[].State.Health' #{ # "Status": "unhealthy", # "FailingStreak": 4, # "Log": [ # ... # { # "Start": "2017-06-27T04:16:27.668441692Z", # "End": "2017-06-27T04:16:27.740937964Z", # "ExitCode": 1, # "Output": "curl: (7) Failed to connect to 127.0.0.1 port 8080: Connection refused\n" # } # ] #}等了15秒多一点,所以健康检查连续4次失败(FailingStreak)。正如预期的那样,docker的状态确实变为“不健康”。但是,只要至少一次健康检查成功,Docker就会将容器恢复到“健康”状态:[root@linuxEA-145 ~]$ curl 127.0.0.1:8081 # Starting up... [root@linuxEA-145 ~]$ docker inspect ec3 | jq '.[].State.Health.Status' # "healthy"使用Docker事件检查运行状况除了直接检查容器状态外,我们还可以听取docker events:[root@linuxEA-145 ~]$ docker events --filter event=health_status # 2017-06-27T00:23:03.691677875-04:00 container health_status: healthy ec36579aa452bf683cb17ee44cbab663d148f327be369821ec1df81b7a0e104b (image=server, name=eager_swartz) # 2017-06-27T00:23:23.998693118-04:00 container health_status: unhealthy ec36579aa452bf683cb17ee44cbab663d148f327be369821ec1df81b7a0e104b (image=server, name=eager_swartz)Docker事件可能有点繁琐,这就是我必须使用的原因--filter。命令本身不会立即退出并保持运行,在事件发生时打印出事件。健康状况和Swarm服务为了尝试运行状况检查如何影响Swarm服务,我暂时将本地Docker实例转换为Swarm模式docker swarm init,现在我可以执行以下操作:[root@linuxEA-145 ~]$ docker service create -p 8080:8080 -p8081:8081 \ --name server \ --health-cmd='curl -sS 127.0.0.1:8080' \ --health-retries=3 \ --health-interval=5s \ server #unable to pin image server to digest: errors: #denied: requested access to the resource is denied #unauthorized: authentication required #ohkvwbsk06vkjyx69434ndqij这使用本地构建的server 镜像将新服务放入Swarm中。Docker对镜像是本地的并返回一堆错误这一事实并不满意,但最终确实返回了新创建的服务的ID:docker service ls #ID NAME MODE REPLICAS IMAGE #ohkvwbsk06vk server replicated 1/1 servercurl 127.0.0.1:8080 将再次工作,发送请求8081将像往常一样关闭服务。但是,这次短时间后端口8080将在没有明确启用服务器的情况下再次开始工作。事情是,只要Swarm注意到容器变得不健康,因此整个服务不再满足所需状态(“运行”),它就会完全关闭容器并启动一个新容器。通过检查我们server 服务的任务集合,我们实际上可以看到它的痕迹 :[root@linuxEA-145 ~]$ docker service ps server #ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS #mt67hkhp7ycr server.1 server moby Running Running 50 seconds ago #pj77brhfhsjm \_ server.1 server moby Shutdown Failed about a minute ago "task: non-zero exit (137): do…"每个Swarm容器都有一个分配给它的任务。当容器死亡时,相应的任务也会关闭,Swarm会创建一对新任务和一个容器。docker service ps 显示给定服务和容器的整个任务链死亡和复活。在我们的特定情况下server,id的初始任务pj77brhfhsjm被标记为失败,docker inspect 甚至说明原因:[root@linuxEA-145 ~]$ docker inspect pj77 | jq '.[].Status.Err' # "task: non-zero exit (137): dockerexec: unhealthy container"“unhealthy container”,这就是原因。但最重要的是,整个服务自动从不健康的状态恢复,几乎没有明显的停机时间Docker运行状况检查是一个小功能,允许将shell命令附加到容器并使用它来检查容器的内容是否足够活跃。对于Docker引擎独立模式下的容器,它只是为它添加健康/不健康的属性(health_status当值更改时加上docker事件),但在Swarm模式下,它实际上将关闭有故障的容器并创建一个停机时间非常短的新容器。
2019年01月14日
4,126 阅读
0 评论
0 点赞
2019-01-13
linuxea:白话容器之dockerfile COPY与ADD的最佳实践(4)(21)
COPY与ADD的最佳实践COPY和ADD是可以将文件放入容器中,那么最好的用法是COPY而不是ADD,当然了,除非文件是tar包并且像自动解压此Dockerfile指令将一个或多个本地文件或文件夹复制到Docker镜像中的目标中。COPY <source>... <destination>COPY ["<source>",... "<destination>"] (包含空格的路径)使用COPY的示例Dockerfile这是COPY在Dockerfile中使用Ruby应用程序的方法。FROM ruby:2.5.1 WORKDIR /usr/src/app COPY Gemfile Gemfile.lock ./ RUN bundle install COPY . . CMD ["./your-daemon-or-script.rb"]它积聚在镜象中的层,先从父镜象ruby:2.5.1,使用所定义FROM。Docker指令WORKDIR为其后面的COPY或者ADD 指令定义工作目录使用COPY它时,会将文件从本地源(在本例 .中为当前目录中的文件)复制到定义的位置WORKDIR。在上面的示例中,第二个 .引用镜象中工作目录中的当前目录。使用COPY创建镜象图层的最佳做法Docker建议使用COPY以在不同镜象层中保存不同文件上下文的方式创建镜象层。这意味着重建镜象是有效的。最不可能更改的文件应位于较低层,而最可能更改的文件应最后添加。如果您有多个Dockerfile步骤使用上下文中的不同文件,则COPY它们是单独的,而不是一次性完成。这可确保每个步骤的构建缓存仅在特定所需文件更改时失效(强制重新执行该步骤)。 编写Dockerfiles的最佳实践上面的Dockerfile示例演示了此原则。通过复制Gemfiles,然后RUN bundle install 使用已安装的Ruby Gems创建一个镜象层,可以对其进行缓存。最后两条Docker指令将应用程序的文件复制到镜象中,并使用默认命令设置CMD。这意味着如果您更改任何应用程序的文件,则可以使用缓存的父级和中间层重建Docker镜像。这比从头开始构建所有这些更有效。为什么不应该使用ADD该ADD指令具有类似的语法COPY。除了将本地文件和目录复制到Docker镜像中的目标之外,它还具有一些附加功能。ADD <source>... <destination>ADD ["<source>",... "<destination>"] (包含空格的路径)但是,Docker的Dockerfile最佳实践官方指南指出,这COPY是ADD大多数用途中的首选指令。一般而言,虽然*ADD* and *COPY*在功能上类似,但是*COPY*是优选的。那是因为它更透明*ADD*。*COPY*仅支持将本地文件基本复制到容器中,同时*ADD*具有一些功能(如仅限本地的tar提取和远程URL支持),这些功能并不是很明显。因此,最好的用途*ADD*是将本地tar文件自动提取到镜象中如:编写Dockerfiles的最佳实践*ADD rootfs.tar.xz /*其中一个ADD附加功能是它可以从URL复制文件,但Docker建议不要将其用于此目的。从URL复制文件的最佳做法如果ADDsource是URL,它将下载文件并将其复制到Docker镜像中的目标位置。docker表明,它往往是效率不高,从使用URL复制ADD,这是最好的做法,以使用其他策略,包括所需的远程文件。由于镜象大小很重要,ADD因此强烈建议不要使用从远程URL获取包。你应该使用curl或wget代替。这样,您可以删除提取后不再需要的文件,也不必在镜象中添加其他图层。 - Dockerfile最佳实践例如,你应该避免做以下事情:ADD http://example.com/big.tar.xz /usr/src/things/ RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things RUN make -C /usr/src/things all而是做一些像:RUN mkdir -p /usr/src/things \ && curl -SL http://example.com/big.tar.xz \ | tar -xJC /usr/src/things \ && make -C /usr/src/things all什么时候可以使用ADD如果<source>是具有可识别压缩格式的本地 tar存档,则它会自动解压缩为Docker镜像中的目录。例如:ADD rootfs.tar.xz /。这是Dockerfiles 中ADDover 的主要推荐用法COPY。对于不需要ADDtar自动提取功能的其他项目(文件,目录),应该始终使用COPY。
2019年01月13日
3,003 阅读
0 评论
0 点赞
2019-01-12
linuxea:白话容器之dockerfile CMD/entrypoint详解3(20)
此前的RUN和CMD,我们知道,RUN是构建的时候运行的命令,在镜像完成后RUN就运行结束。随后推送到仓库中,这些RUN命令是不会在进行运行的。- init在Docker上下文中,一个Docker只会运行一个应用程序,那么应该运行那个程序,又是什么应用?一般情况下,能拥有生产能力的应用通常在宿主机上一般表现是运行在后台守护进程程序,如:mysql,nginx等。这些服务在运行时候,都是以某个进程运行。某个进程都应该是某个进程的子进程,除init之外,而init是由内核启动的,一般我们在启动一个进程的时候,是以shell的子进程运行的,在命令行下创建的任何进程都是shell的子进程,而有一些经常也会直接占据shell的终端设备,就算使用&放置后台,启动的父进程也仍然是shell。进程终止的时候会将所有的子进程销毁,这种情况下我们会使用nohub command &,这样一来就类似于将启动的进程init那么在Docker中运行的init进程(init的id是1)是由内核启动,还是托管shell启 动。如果基于内核启动 ls /etc/* ,|等shell特性是无法使用的,那么如果基于shell启动,那init的id就不再是1了exec假如想基于shell的方式来启动一个主进程,那么shell的id号就是1,而后基于此在启动主进程,但是这样一来shell就不能退出,那可能需要一种能够剥离终端的方式启动,但是剥离了终端的方式启动,主进程号又不是1了。不过,我们可以使用exec来解决,shell启动是没有问题,进程号id是1也没有关系,exec顶替shell的id为1,取代shell进程,shell退出后exec就成了id为1的进程。在很多时候,在容器内启动一个应用程序的时候可以不基于shell,直接启动也可以,也可以基于shell,如果基于shell启动,并且不违背shell主进程id为1的调节关系,那么就可以使用第二种方式,exec。CMDRUN是构建的镜象build时候执行的,而cmd是定义一个镜象文件启动为容器时候默认要运行的程序,而Docker容器默认运行一个程序,在运行CMD的时候,是可以写多条CMD的,而最后一条CMD是生效的。而RUN是可以从上倒下接多RUN命令逐一运行。CMD类属于RUN命令,CMD指令也可以用于运行任何命令或应用程序,不过,二者的运行时间点不同RUN指令运行与映像文件构建过程中,而CMD指令运行于基于Dockerfile构建出的新映像文件启动一个容器时CMD指令的首要目的在于为启动的容器指定默认要运行的程序,且运行结束后,容器也将终止;不过,CMD指令的命令其可以被Docker run命令选项所覆盖在Dockerfile中可以存在多个CMD指令,但仅最后一个会生效命令CMD <command> CMD ["<executable>","<paraml>","<param2>"] CMD ["<param1>","<param2>"]前两种语法格式的意义同RUN第一种的CMD的命令执行是直接写命令的,并且PID不为1,也无法接收信号(接收信号的必然是pid为1的超级管理进程),docker stop也无法停止。第二种直接启动为ID为1的进程,可接受处理shell信号的。第三种则用于ENTRYPOINT指令提供默认参数编写Dockerfile如,创建目录后追加文件,最后用CMD直接调用httpd启动FROM busybox LABEL maintainer="linuxea.com" app="CMD" ENV WEB_ROOT="/data/wwwroot" RUN mkdir -p ${WEB_ROOT} \ && echo '<h1> helo linuxea .</h1>' >> ${WEB_ROOT}/index.html CMD /bin/httpd -f -h ${WEB_ROOT}开始build[root@linuxEA /data/linuxea2]$ docker build -t marksugar/httpd:9 Sending build context to Docker daemon 2.048kB Step 1/5 : FROM busybox ---> 59788edf1f3e Step 2/5 : LABEL maintainer="linuxea.com" app="CMD" ---> Running in b6e91f2461dd Removing intermediate container b6e91f2461dd ---> 53559ed7015a Step 3/5 : ENV WEB_ROOT="/data/wwwroot" ---> Running in 3e615febfd44 Removing intermediate container 3e615febfd44 ---> a7917cb7ecbb Step 4/5 : RUN mkdir -p ${WEB_ROOT} && echo '<h1> helo linuxea .</h1>' >> ${WEB_ROOT}/index.html ---> Running in 15153c929109 Removing intermediate container 15153c929109 ---> 8e5548f3c00a Step 5/5 : CMD /bin/httpd -f -h ${WEB_ROOT} ---> Running in feeb34a9c423 Removing intermediate container feeb34a9c423 ---> a091b6d8a31d Successfully built a091b6d8a31d Successfully tagged marksugar/httpd:9从这里可以看到,这条启动命令是/bin/sh启动的子进程,在此后启动的时候会替换成id1,也就是默认执行exec将/bin/sh替换掉[root@linuxEA /data/linuxea2]$ docker inspect marksugar/httpd:9 ... "Cmd": [ "/bin/sh", "-c", "/bin/httpd -f -h ${WEB_ROOT}" ... 而后run起来,但是这里是没有交互式接口的,尽管使用了-it[root@linuxEA /data/linuxea2]$ docker run --name linuxea --rm -it marksugar/httpd:9 不过,可以使用exec进入容器,/bin/httpd -f -h /data/wwwroot的id为1我们在Dockerfile中直接使用命令的方式避免他不是1,那么这里就直接启动为1,默认执行力exec替换。这也就说明了,尽管使用-it仍然进入不了容器的原因,init1的进程不是shell。进入就要在使用exec绕过进入[root@linuxEA ~]$ docker exec -it linuxea sh / # ps aux PID USER TIME COMMAND 1 root 0:00 /bin/httpd -f -h /data/wwwroot 7 root 0:00 sh 13 root 0:00 ps aux / # 第二种格式CMD ["/bin/httpd","-f","-h ${WEB_ROOT}"]以这种方式进行buildFROM busybox LABEL maintainer="linuxea.com" app="CMD" ENV WEB_ROOT="/data/wwwroot" RUN mkdir -p ${WEB_ROOT} \ && echo '<h1> helo linuxea .</h1>' >> ${WEB_ROOT}/index.html #CMD /bin/httpd -f -h ${WEB_ROOT} CMD ["/bin/httpd","-f","-h ${WEB_ROOT}"]启动就会报错No such file[root@linuxEA /data/linuxea2]$ docker run --name linuxea --rm -it marksugar/httpd:10 httpd: can't change directory to ' ${WEB_ROOT}': No such file or directory报错No such file是因为CMD ["/bin/httpd","-f","-h ${WEB_ROOT}"]并不会运行成shell的子进程,而此变量是shell的变量,内核却不知道这个路径,所以会报错。不过,我们可以指定为shell,如: CMD ["/bin/sh","-c","/bin/httpd","-f","-h ${WEB_ROOT}"]引言此前我们使用一条命令运行容器的时候,CMD的指令是可以被覆盖的,如下[root@linuxEA ~]$ docker run --name linuxea --rm -it marksugar/httpd:9 ls /etc group hosts mtab passwd shadow hostname localtime network resolv.conf上面这条命令是说,运行这个容器,ls /etc覆盖了此前镜像中的CMD中的启动httpd的命令。但是有时候我们不希望被覆盖,就使用ENTRYPOINTENTRYPOINT类似于CMD指令的功能,用于为容器指定默认的运行程序,从而使得容器像是一个单独的可执行文件与CMD不同的是由ENTRYPOINT启动的程序不会被docker run命令行指定的参数所覆盖,而且,这些命令行参数会被当作参数传递给ENTRYPOINT指令的指定程序 不过,docker run命令--entrypoint选项参数可覆盖ENTRYPOINT指令指定的程序ENTRYPOINT <command> ENTRYPOINT ["<executable>","<param1>","<param2>"]docker run命令传入的命令参数会覆盖CMD指令的内容并且附加到ENTRYPOINT命令最后作为其参数使用Dockerfile文件中也可以存在多个ENTRYPOINT指令,但仅有最后一个生效我们先编写一个Dockerfile,使用NETRYPOINT启动FROM busybox LABEL maintainer="linuxea.com" app="CMD" ENV WEB_ROOT="/data/wwwroot" RUN mkdir -p ${WEB_ROOT} \ && echo '<h1> helo linuxea .</h1>' >> ${WEB_ROOT}/index.html ENTRYPOINT /bin/httpd -f -h ${WEB_ROOT}而后build[root@linuxEA /data/linuxea2]$ docker build -t marksugar/httpd:11 . Sending build context to Docker daemon 2.048kB Step 1/5 : FROM busybox ---> 59788edf1f3e Step 2/5 : LABEL maintainer="linuxea.com" app="CMD" ---> Using cache ---> 53559ed7015a Step 3/5 : ENV WEB_ROOT="/data/wwwroot" ---> Using cache ---> a7917cb7ecbb Step 4/5 : RUN mkdir -p ${WEB_ROOT} && echo '<h1> helo linuxea .</h1>' >> ${WEB_ROOT}/index.html ---> Using cache ---> 8e5548f3c00a Step 5/5 : ENTRYPOINT /bin/httpd -f -h ${WEB_ROOT} ---> Running in 34c028efac0d Removing intermediate container 34c028efac0d ---> b7be6f74fc65 Successfully built b7be6f74fc65 Successfully tagged marksugar/httpd:11启动是没有问题的[root@linuxEA /data/linuxea2]$ docker run --name linuxea --rm -it marksugar/httpd:11我们获取到这个ip。访问试试[root@linuxEA ~]$ docker inspect -f {{.NetworkSettings.IPAddress}} linuxea 192.168.100.2[root@linuxEA ~]$ curl 192.168.100.2 <h1> helo linuxea .</h1>ENTRYPOINT而后使用CMD的方式同样来覆盖[root@linuxEA /data/linuxea2]$ docker run --name linuxea --rm -it marksugar/httpd:11 ls /etc容器依然运行起来,但我们并没有看到ls /etc的内容。这是因为在run的时候使用了ls /etc并不会替换Dockerfile中ENTRYPOINT的运行命令,只是在ENTRYPOINT命令之后加了ls /etc,而httpd识别不出ls /etc而已 如果一定要进行覆盖,就需要使用--entrypoint,如下:docker run --name linuxea --rm -it --entrypoint "/bin/ls" marksugar/httpd:11 -al /etc[root@linuxEA ~]$ docker run --name linuxea --rm -it --entrypoint "/bin/ls" marksugar/httpd:11 -al /etc total 28 drwxr-xr-x 1 root root 66 Dec 8 09:07 . drwxr-xr-x 1 root root 6 Dec 8 09:07 .. -rw-rw-r-- 1 root root 307 Sep 6 20:11 group -rw-r--r-- 1 root root 13 Dec 8 09:07 hostname -rw-r--r-- 1 root root 177 Dec 8 09:07 hosts -rw-r--r-- 1 root root 127 May 4 2018 localtime lrwxrwxrwx 1 root root 12 Dec 8 09:07 mtab -> /proc/mounts drwxr-xr-x 6 root root 79 Oct 1 22:37 network -rw-r--r-- 1 root root 340 Sep 6 20:11 passwd -rw-r--r-- 1 root root 114 Dec 8 09:07 resolv.conf -rw------- 1 root root 243 Sep 6 20:11 shadow示例ENTRYPOINT此时我们知道ENTRYPOINT是作为入口点的指令,通过exec 指定,指定的命令和参数作为一个JSON数组,那就意味着需要使用双引号而不是单引号ENTRYPOINT ["executable", "param1", "param2"]使用此语法,Docker将不使用命令shell,这意味着不会发生正常的shell处理。如果需要shell处理功能,则可以使用shell命令启动JSON数组。ENTRYPOINT [ "sh", "-c", "echo $HOME" ]另一种选择是使用脚本来运行容器的入口点命令。按照惯例,它通常在名称中包含入口点。在此脚本中,您可以设置应用程序以及加载任何配置和环境变量。下面是一个如何使用ENTRYPOINT exec语法在Dockerfile中运行它的示例。COPY ./docker-entrypoint.sh / ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["postgres"]例如,Postgres官方图像使用以下脚本作为其ENTRYPOINT:#!/bin/bash set -e if [ "$1" = 'postgres' ]; then chown -R postgres "$PGDATA" if [ -z "$(ls -A "$PGDATA")" ]; then gosu postgres initdb fi exec gosu postgres "$@" fi exec "$@"docker-compose 的写法:Docker Compose文件中使用的命令是相同的,除了使用小写字母。entrypoint: /code/entrypoint.sh可以在docker-compose.yml中使用列表定义入口点。entrypoint: - php - -d - zend_extension=/usr/local/lib/php/xdebug.so - -d - memory_limit=-1 - vendor/bin/phpunit不过仍然可可以使用docker run --entrypoint或docker-compose run --entrypoint标记覆盖入口的指令CMD/commandCMD(Dockerfiles)/ command(Docker Compose文件)的主要目的是在执行容器时提供默认值。这些将在入口点之后被附加到入口的参数。例如,如果运行docker run <image>,则将执行Dockerfiles中CMD/所指定的命令和参数command。在Dockerfiles中,可以定义CMD包含可执行文件的默认值。例如:CMD ["executable","param1","param2"]如果省略了可执行文件,则还必须指定一条ENTRYPOINT指令。CMD ["param1","param2"] (作为ENTRYPOINT的默认参数)注意:其中只能有一条CMD指令Dockerfile。如果列出多个CMD,则只有最后一个CMD生效。Docker Compose命令使用Docker Compose时,可以在docker-compose.yml中定义相同的指令,但它以小写形式写成完整的单词command。command: ["bundle", "exec", "thin", "-p", "3000"]覆盖CMD可以覆盖CMD运行容器时指定的命令。docker run rails_app rails console如果指定了参数docker run,那么它们将覆盖指定的默认值CMD。语法最佳实践还有EXEC语法,shell语法两个另一个有效的选项ENTRYPOINT和CMD。这将以字符串形式执行此命令并执行变量替换。ENTRYPOINT command param1 param2 CMD command param1 param2CMD应该几乎总是以形式使用CMD [“executable”, “param1”, “param2”…]。因此,如果镜象是用于服务的,例如Apache和Rails,那么你可以运行类似的东西CMD ["apache2","-DFOREGROUND"]`。实际上,建议将这种形式的指令用于任何基于服务的镜象。所述*ENTRYPOINT*shell形式防止任何*CMD*或*run*被使用命令行参数覆盖,但是有缺点,*ENTRYPOINT*将被开始作为一个子命令*/bin/sh -c*,其不通过信号。这意味着可执行文件将不是容器*PID 1*- 并且不会收到Unix信号 - 因此您的可执行文件将不会收到*SIGTERM*来自*docker stop <container>*如果*CMD*用于为*ENTRYPOINT*指令提供默认参数,则应使用JSON数组格式指定*CMD*和*ENTRYPOINT*指令。BothCMD和ENTRYPOINTinstructions指定运行容器时执行的命令。很少有规则描述它们如何相互作用。Dockerfiles应至少指定一个CMD或ENTRYPOINT命令。ENTRYPOINT 应该在将容器用作可执行文件时定义。CMD应该用作定义ENTRYPOINT命令的默认参数或在容器中执行ad-hoc命令的方法。CMD 在使用替代参数运行容器时将被覆盖。延伸阅读 :https://docs.docker.com/engine/reference/builder/ https://docs.docker.com/develop/develop-images/dockerfile_best-practices/ https://docs.docker.com/engine/reference/builder/#usage https://docs.docker.com/compose/compose-file/ https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
2019年01月12日
3,536 阅读
0 评论
0 点赞
2019-01-11
linuxea:白话容器之使用dockerfile指令使用2(19)
ADDADD与COPY很相似,ADD支持几使用tar文件和URL路径ADD <src> ... <dest> ADD ["<src>",..."<dest>"]如果<src>为URL且<dest>不是/结尾,则<src>指定的文件将被下载并直接被创建为<dest>,如果<dest>以/结尾,则文件名URL指定的文件将被直接下载并保存为<dest>/<filename>如果<src>是一个本地的压缩tar文件,他将被展开成一个目录,其行为类似于"tar -x"命令;然而,通过URL获取到的tar文件将不会自动展开如果<src>有多个,或其间接或直接使用了通配符,则<dest>必须是一个以/结尾的目录路径;如果<dest>不以/结尾,则其被视作一个普通文件,<src>的内容将被直接写入到<dest>除此之外,ADD和COPY一样示例我們下載一個RUL的nginx到/usr/local/src/,来验证是否会被解压[root@linuxEA_10_10_240_145 /data/linuxea]$ cat Dockerfile # describe : test FROM busybox:latest MAINTAINER www.linuxea.com LABEL maintainer="www.linuxea.com" COPY index.html /data/wwwroot/index.html COPY zabbix /data/wwwroot/zabbix/ ADD http://10.10.240.145/nginx-1.14.0.tar.gz /usr/local/src/而后build[root@linuxEA_10_10_240_145 /data/linuxea]$ docker build -t marksugar/httpd:2 ./ Sending build context to Docker daemon 33.28kB Step 1/6 : FROM busybox:latest ---> 59788edf1f3e Step 2/6 : MAINTAINER www.linuxea.com ---> Using cache ---> 7f12e197dfba Step 3/6 : LABEL maintainer="www.linuxea.com" ---> Using cache ---> c28e9c23ae2f Step 4/6 : COPY index.html /data/wwwroot/index.html ---> Using cache ---> f51ffe499c66 Step 5/6 : COPY zabbix /data/wwwroot/zabbix/ ---> Using cache ---> eb31fdcfcd13 Step 6/6 : ADD http://10.10.240.145/nginx-1.14.0.tar.gz /usr/local/src/ Downloading [==================================================>] 1.016MB/1.016MB ---> 0b2794e2984d Successfully built 0b2794e2984d Successfully tagged marksugar/httpd:3而后进入容器查看,这个包事实上并没有被解压[root@linuxEA_10_10_240_145 /data/linuxea]$ docker run --name linuxea1 --rm marksugar/httpd:3 ls /usr/local/src WARNING: IPv4 forwarding is disabled. Networking will not work. nginx-1.14.0.tar.gz接着在验证下本地的tag解压的方式,如下:但是,有一个前提是我们要先现在这个nginx的tar包在本地[root@linuxEA_10_10_240_145 /data/linuxea]$ wget http://10.10.240.145/nginx-1.14.0.tar.gz --2018-12-06 21:24:39-- http://10.10.240.145/nginx-1.14.0.tar.gz 正在连接 10.10.240.145:80... 已连接。 已发出 HTTP 请求,正在等待回应... 200 OK 长度:1016272 (992K) [application/octet-stream] 正在保存至: “nginx-1.14.0.tar.gz” 100%[=====================================================================================================================>] 1,016,272 --.-K/s 用时 0.001s 2018-12-06 21:24:39 (688 MB/s) - 已保存 “nginx-1.14.0.tar.gz” [1016272/1016272])那就成了这个样子[root@linuxEA_10_10_240_145 /data/linuxea]$ cat Dockerfile # describe : test FROM busybox:latest MAINTAINER www.linuxea.com LABEL maintainer="www.linuxea.com" COPY index.html /data/wwwroot/index.html COPY zabbix /data/wwwroot/zabbix/ ADD nginx-1.14.0.tar.gz /usr/local/src/在build,在看[root@linuxEA_10_10_240_145 /data/linuxea]$ docker build -t marksugar/httpd:4 ./ Sending build context to Docker daemon 1.05MB Step 1/6 : FROM busybox:latest ---> 59788edf1f3e Step 2/6 : MAINTAINER www.linuxea.com ---> Using cache ---> 7f12e197dfba Step 3/6 : LABEL maintainer="www.linuxea.com" ---> Using cache ---> c28e9c23ae2f Step 4/6 : COPY index.html /data/wwwroot/index.html ---> Using cache ---> f51ffe499c66 Step 5/6 : COPY zabbix /data/wwwroot/zabbix/ ---> Using cache ---> eb31fdcfcd13 Step 6/6 : ADD nginx-1.14.0.tar.gz /usr/local/src/ ---> d65a535b0842 Successfully built d65a535b0842 Successfully tagged marksugar/httpd:4在查看,已经被解压[root@linuxEA_10_10_240_145 /data/linuxea]$ docker run --name linuxea1 --rm marksugar/httpd:4 ls /usr/local/src WARNING: IPv4 forwarding is disabled. Networking will not work. nginx-1.14.0 [root@linuxEA_10_10_240_145 /data/linuxea]$ docker run --name linuxea1 --rm marksugar/httpd:4 ls /usr/local/src/nginx-1.14.0 WARNING: IPv4 forwarding is disabled. Networking will not work. CHANGES CHANGES.ru LICENSE README auto conf configure contrib html man src [root@linuxEA_10_10_240_145 /data/linuxea]$ WORKDIR用于指定Dockerfile中所有的RUN,CMD,ENTRYPOINT,COPY和ADD指定设定工作目录WORKDIR <dirpath>在Dockerfile文件中,WORKDIR指令可出现多次,其路径也可以为相对路径,不过,其实相对此前一个WORKDIR指令指定的路径另外,WORKDIR也可调用ENV指定定义的变量、WORKDIR /var/log WORKDIR $STATEPATH像这样WORKDIR /usr/local/src/ ADD nginx-1.14.0.tar.gz ./WORKDIR进入/usr/local/src/目录,而后ADD nginx-1.14.0.tar.gz ./ 将nginx-1.14.0.tar.gz解压到当前目录,那当前目录也就是/usr/local/src/目录如果WORKDIR做了相对路径引用,逆序查找顺序的时候,逆序的第一个WORKDIR的位置就是他的工作目录。Volume此前,我们了解到,绑定挂载卷上可以指定容器和宿主机的目录的。但是,在dockerfile中的自动指定的时候,只能指定挂载点。volume用于在image中创建一个挂载点目录,以挂载Docker host上的卷或其他容器上的卷VOLUME <mountpoint> 或 VOLUME ["<mountpoint>"]如果挂载点目录路径下此前的文件存在,docker run命令会在卷挂载完成后将此前的文件复制到新挂载的卷中我们将VOLUME /data/mariadb/挂载[root@linuxEA_10_10_240_145 /data/linuxea]$ cat Dockerfile # describe : test FROM busybox:latest MAINTAINER www.linuxea.com LABEL maintainer="www.linuxea.com" COPY index.html /data/wwwroot/index.html COPY zabbix /data/wwwroot/zabbix/ WORKDIR /usr/local/src/ ADD nginx-1.14.0.tar.gz ./ VOLUME /data/mariadb/重新构建[root@linuxEA_10_10_240_145 /data/linuxea]$ docker build -t marksugar/httpd:5 ./ Sending build context to Docker daemon 1.05MB Step 1/8 : FROM busybox:latest ---> 59788edf1f3e Step 2/8 : MAINTAINER www.linuxea.com ---> Using cache ---> 7f12e197dfba Step 3/8 : LABEL maintainer="www.linuxea.com" ---> Using cache ---> c28e9c23ae2f Step 4/8 : COPY index.html /data/wwwroot/index.html ---> Using cache ---> f51ffe499c66 Step 5/8 : COPY zabbix /data/wwwroot/zabbix/ ---> Using cache ---> eb31fdcfcd13 Step 6/8 : WORKDIR /usr/local/src/ Removing intermediate container 805c9be248af ---> 1659fdf0e432 Step 7/8 : ADD nginx-1.14.0.tar.gz ./ ---> bcff476b6d7c Step 8/8 : VOLUME /data/mariadb/ ---> [Warning] IPv4 forwarding is disabled. Networking will not work. ---> Running in 2112dcfe986a Removing intermediate container 2112dcfe986a ---> 664c3d943228 Successfully built 664c3d943228 Successfully tagged marksugar/httpd:5run起来查看下是否被挂载[root@linuxEA_10_10_240_145 /data/linuxea]$ docker run --name linuxea1 --rm marksugar/httpd:5 mount|grep mariadb WARNING: IPv4 forwarding is disabled. Networking will not work. /dev/mapper/DTVG-root on /data/mariadb type xfs (rw,relatime,attr2,inode64,noquota)也可以这样看[root@linuxEA_10_10_240_145 ~]$ docker inspect linuxea1 ... "Mounts": [ { "Type": "volume", "Name": "49a020511459b54042ce42cfca7717455ced52b17d04ab8d92770832d4e8ab5a", "Source": "/var/lib/docker/volumes/49a020511459b54042ce42cfca7717455ced52b17d04ab8d92770832d4e8ab5a/_data", "Destination": "/data/mariadb", "Driver": "local", "Mode": "", "RW": true, "Propagation": "" } ], ... Voulume被定义在Dockerfile中,在启动的时候就不需要定义的,就会自动有一个存储卷,不过这个存储卷在宿主机的位置,用的是docker管理的路径格式。EXPOSEEXPOSE将容器内的已经监听的端口暴露出去,用于为容器打开指定的监听端口以实现与外部通信EXPOSE <port>[/<protocol>][<promt>[/<protocol>] ...]<protocol>用于指定传输层协议,可为tcp或udp二者之一,默认为tcp协议EXPOSE默认可以一次指定多个端口,如:EXPOSE 11211/tcp 11211/udp其中会自动生成一条DNAT规则,假如你使用的是默认网络但是,这里只是负责暴露容器内监听的端口,并不意味着设置EXPOSE就已经暴露到宿主机。至于到了宿主机上是什么端口,如果不指定,那就是随机的。一般情况下,在docker run的时候加-大P指定-P表示要暴露的端口,而要暴露的端口是在镜象内EXPOSE配置的端口。-P 11211:11211我们试图暴露[root@linuxEA_10_10_240_145 /data/linuxea]$ cat Dockerfile # describe : test FROM busybox:latest MAINTAINER www.linuxea.com LABEL maintainer="www.linuxea.com" COPY index.html /data/wwwroot/index.html COPY zabbix /data/wwwroot/zabbix/ WORKDIR /usr/local/src/ ADD nginx-1.14.0.tar.gz ./ VOLUME /data/mariadb/ EXPOSE 80/tcpbuild[root@linuxEA_10_10_240_145 /data/linuxea]$ docker build -t marksugar/httpd:6 ./ Sending build context to Docker daemon 1.05MB Step 1/9 : FROM busybox:latest ---> 59788edf1f3e Step 2/9 : MAINTAINER www.linuxea.com ---> Using cache ---> 7f12e197dfba Step 3/9 : LABEL maintainer="www.linuxea.com" ---> Using cache ---> c28e9c23ae2f Step 4/9 : COPY index.html /data/wwwroot/index.html ---> Using cache ---> f51ffe499c66 Step 5/9 : COPY zabbix /data/wwwroot/zabbix/ ---> Using cache ---> eb31fdcfcd13 Step 6/9 : WORKDIR /usr/local/src/ ---> Using cache ---> 1659fdf0e432 Step 7/9 : ADD nginx-1.14.0.tar.gz ./ ---> Using cache ---> bcff476b6d7c Step 8/9 : VOLUME /data/mariadb/ ---> Using cache ---> 664c3d943228 Step 9/9 : EXPOSE 80/tcp ---> [Warning] IPv4 forwarding is disabled. Networking will not work. ---> Running in 1b82b3db2f99 Removing intermediate container 1b82b3db2f99 ---> 23efb60395d4 Successfully built 23efb60395d4 Successfully tagged marksugar/httpd:6而后我们run起这个容器,并且使用httpd -f -h /data/wwwroot/启动httpd[root@linuxEA_10_10_240_145 /data/linuxea]$ docker run --name linuxea1 --rm marksugar/httpd:6 httpd -f -h /data/wwwroot/ WARNING: IPv4 forwarding is disabled. Networking will not work.获取到此容器的ip[root@linuxEA_10_10_240_145 ~]$ docker inspect -f {{.NetworkSettings.Networks.bridge.IPAddress}} linuxea1 192.168.100.2访问试试[root@linuxEA_10_10_240_145 ~]$ curl 192.168.100.2 linuxea-2018年 12月 06日 星期四 19:52:14 CST当然,你也可以这样[root@linuxEA_10_10_240_145 ~]$ curl $(docker inspect -f {{.NetworkSettings.Networks.bridge.IPAddress}} linuxea1) linuxea-2018年 12月 06日 星期四 19:52:14 CST-P那么此时也是没有暴露端口的,因为没有加-p选项,没有-p选项就是待暴露,但是尚未暴露[root@linuxEA_10_10_240_145 ~]$ docker port linuxea1 [root@linuxEA_10_10_240_145 ~]$ 那现在可以使用-P指定,也就是随机的[root@linuxEA_10_10_240_145 /data/linuxea]$ docker run --name linuxea1 --rm -P marksugar/httpd:6 httpd -f -h /data/wwwroot/ WARNING: IPv4 forwarding is disabled. Networking will not work.在看[root@linuxEA_10_10_240_145 ~]$ docker port linuxea1 80/tcp -> 0.0.0.0:1024我们通过宿主机的ip访问1024[root@linuxEA_10_10_240_145 ~]$ curl 10.10.240.145:1024 linuxea-2018年 12月 06日 星期四 19:52:14 CST有些乱码,我们暂且不管-p也可以使用-p 使用-p 81:80,将容器的80暴露到宿主机的81端口[root@linuxEA_10_10_240_145 /data/linuxea]$ docker run --name linuxea1 --rm -p 81:80 marksugar/httpd:6 httpd -f -h /data/wwwroot/ WARNING: IPv4 forwarding is disabled. Networking will not work.[root@linuxEA_10_10_240_145 ~]$ docker port linuxea1 80/tcp -> 0.0.0.0:81[root@linuxEA_10_10_240_145 ~]$ curl 10.10.240.145:81 linuxea-2018年 12月 06日 星期四 19:52:14 CSTENVENV用于镜象定义所需的环境变量,并可被Dockerfile文件中位于其后的其他指令(如ENV,ADD,COPY等)所调用调用格式为$varibale_name或${varibale_name}ENV <key> <value> 或 ENV <key>=<value>第一种格式中,<key>之后的所有内容均会被起视作为<value>的组成部分,因此,一次只能设置一个变量第二种格式可用一次是设置多个变量,每个变量作为一个"<key>=<value>"的键值对,如果<value>中包含空格,可以以反射线(\)进行转义,也可以通过对<value>加引号进行标识;另外,反斜线也可用于续行定义多个变量的时候,可以使用第二种方式,以便于在同一层中完成所有功能我们添加几个变量而后在中应用,如下ENV NG_PATH /usr/local/src/ ENV NG_VERSION="1.14.0" \ ZABBIX=/data/wwwroot/zabbix/引用COPY zabbix $ZABBIX WORKDIR ${NG_PATH:-/usr/local/src/} ADD nginx-${NG_VERSION:-1.14.0}.tar.gz ./ ${NG_PATH:-/usr/local/src/}这里可以用${NG_PATH:-}的方式,也就是说,如果没有变量或者变量为空值,默认参数就是/usr/local/src/,最终如下:[root@linuxEA_10_10_240_145 /data/linuxea]$ cat Dockerfile # describe : test FROM busybox:latest MAINTAINER www.linuxea.com ENV NG_PATH /usr/local/src/ ENV NG_VERSION="1.14.0" \ ZABBIX=/data/wwwroot/zabbix/ LABEL maintainer="www.linuxea.com" COPY index.html /data/wwwroot/index.html COPY zabbix $ZABBIX WORKDIR ${NG_PATH:-/usr/local/src/} ADD nginx-${NG_VERSION:-1.14.0}.tar.gz ./ VOLUME /data/mariadb/ EXPOSE 80/tcp而后buildroot@linuxEA_10_10_240_145 /data/linuxea]$ docker build -t marksugar/httpd:7 ./ Sending build context to Docker daemon 1.05MB Step 1/11 : FROM busybox:latest ---> 59788edf1f3e Step 2/11 : MAINTAINER www.linuxea.com ---> Using cache ---> 7f12e197dfba Step 3/11 : ENV NG_PATH /usr/local/src/ ---> Running in e228dd6984c8 Removing intermediate container e228dd6984c8 ---> e6c199578807 Step 4/11 : ENV NG_VERSION="1.14.0" ZABBIX=/data/wwwroot/zabbix/ ---> Running in 0322efbf9c4c Removing intermediate container 0322efbf9c4c ---> c0b20f2d3726 Step 5/11 : LABEL maintainer="www.linuxea.com" ---> Running in f5fdceb54d99 Removing intermediate container f5fdceb54d99 ---> 9236122c20ae Step 6/11 : COPY index.html /data/wwwroot/index.html ---> f13e97d53c76 Step 7/11 : COPY zabbix $ZABBIX ---> 4b46be31391c Step 8/11 : WORKDIR ${NG_PATH:-/usr/local/src/} Removing intermediate container 4ca70ebc77a9 ---> 48b53e685832 Step 9/11 : ADD nginx-${NG_VERSION:-1.14.0}.tar.gz ./ ---> d785091af380 Step 10/11 : VOLUME /data/mariadb/ ---> Running in fb2d1a8ccdbf Removing intermediate container fb2d1a8ccdbf ---> 7d93a3a2d717 Step 11/11 : EXPOSE 80/tcp ---> Running in 282a1384d563 Removing intermediate container 282a1384d563 ---> 5f6b1362d52d Successfully built 5f6b1362d52d Successfully tagged marksugar/httpd:7run起来验证下,变量被引用ok[root@linuxEA_10_10_240_145 ~]$ docker run --name linuxea1 --rm -p 81:80 marksugar/httpd:7 ls /usr/local/src nginx-1.14.0[root@linuxEA_10_10_240_145 ~]$ docker run --name linuxea1 --rm -p 81:80 marksugar/httpd:7 ls /data/wwwroot/zabbix scripts zabbix_agentd.conf zabbix_agentd.d那么这些ENV定义在Dockerfile中,而后在启动的容器中是已经存在[root@linuxEA_10_10_240_145 ~]$ docker run --name linuxea1 --rm -p 81:80 marksugar/httpd:7 printenv PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=af3e048f900f NG_PATH=/usr/local/src/ NG_VERSION=1.14.0 ZABBIX=/data/wwwroot/zabbix/ HOME=/root尽管在build的时候,已经使用了一次这些环境变量构建了此前Dockerfile中变量值的构建,并且完成。但是,这些变量仍然在docker容器中,在run启动的时候是可以被重新赋值的。这个赋值当然是不会影响到此前构建过的惊喜,如果此时第二次调用被重新赋值过的变量,就会得到新的变量赋值这次我们将之前的NG_VERSION=1.14.0改成 NG_VERSION=1.15.2,只需要在启动的时候使用-e,如:-e NG_VERSION="1.15.2"。[root@linuxEA_10_10_240_145 ~]$ docker run --name linuxea1 --rm -p 81:80 -e NG_VERSION="1.15.2" marksugar/httpd:7 printenv PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=df943feee8b7 NG_VERSION=1.15.2 NG_PATH=/usr/local/src/ ZABBIX=/data/wwwroot/zabbix/ HOME=/rootRUN用于指定docker build过程中运行的程序,可以说任何命令RUN <command> RUN ["<executable>","<param1>","<param2>"]第一种格式中,<command>通常是一个shell命令,且以“/bin/sh -c”来运行它,这意味着此进程在容器中的pid不为1,不能接受unix信号。因此,当使用docker stop <container>命令停止容器时,此进程接收不到SIGTERM信号;第二种语法格式中的参数是一个json格式的数组,其中<executable>为要运行的命令,后面的<paramN>为传递给命令的选项或参数;然而,此种格式指令的命令不会以"/bin/sh -c "来发起,因此常见的shell操作如变量替换以及通配符(?,*等)替换将不会进行;不过,如果要运行的命令依赖于此shell特性的话,可以将其替换为类似下面的格式。RUN ["/bin/bash","-c","<executable>","<param1>"]RUN和CMD,RUN是在docker build中运行,也就说在构建镜像时候build过程中运行的命令;而CMD是在镜像启动的时候运行的命令,如果没有指定运行的命令,CMD就是默认运行的命令我们加一段RUN命令,内容是下载nginx安装包,而后解压,并且更名,主要体验下RUN命令使用RUN cd /usr/local \ && wget http://10.10.240.145/nginx-${NG_VERSION}.tar.gz \ && tar -xf nginx-${NG_VERSION}.tar.gz \ && mv nginx-${NG_VERSION} nginx# describe : test FROM busybox:latest MAINTAINER www.linuxea.com ENV NG_PATH /usr/local/src/ ENV NG_VERSION="1.14.0" \ ZABBIX=/data/wwwroot/zabbix/ LABEL maintainer="www.linuxea.com" COPY index.html /data/wwwroot/index.html COPY zabbix $ZABBIX WORKDIR ${NG_PATH:-/usr/local/src/} ADD nginx-${NG_VERSION:-1.14.0}.tar.gz ./ VOLUME /data/mariadb/ RUN cd /usr/local \ && wget http://10.10.240.145/nginx-${NG_VERSION}.tar.gz \ && tar -xf nginx-${NG_VERSION}.tar.gz \ && mv nginx-${NG_VERSION} nginx EXPOSE 80/tcp而后进行build,验证下RUN命令中的操作是否被正常执行了[root@linuxEA_10_10_240_145 /data/linuxea]$ docker build -t marksugar/httpd:8 ./ Sending build context to Docker daemon 1.051MB Step 1/12 : FROM busybox:latest ---> 59788edf1f3e Step 2/12 : MAINTAINER www.linuxea.com ---> Using cache ---> 7f12e197dfba Step 3/12 : ENV NG_PATH /usr/local/src/ ---> Using cache ---> e6c199578807 Step 4/12 : ENV NG_VERSION="1.14.0" ZABBIX=/data/wwwroot/zabbix/ ---> Using cache ---> c0b20f2d3726 Step 5/12 : LABEL maintainer="www.linuxea.com" ---> Using cache ---> 9236122c20ae Step 6/12 : COPY index.html /data/wwwroot/index.html ---> Using cache ---> f13e97d53c76 Step 7/12 : COPY zabbix $ZABBIX ---> Using cache ---> 4b46be31391c Step 8/12 : WORKDIR ${NG_PATH:-/usr/local/src/} ---> Using cache ---> 48b53e685832 Step 9/12 : ADD nginx-${NG_VERSION:-1.14.0}.tar.gz ./ ---> Using cache ---> d785091af380 Step 10/12 : VOLUME /data/mariadb/ ---> Using cache ---> 7d93a3a2d717 Step 11/12 : RUN cd /usr/local && wget http://10.10.240.145/nginx-${NG_VERSION}.tar.gz && tar -xf nginx-${NG_VERSION}.tar.gz && mv nginx-${NG_VERSION} nginx ---> Running in 3b8cb3885487 Connecting to 10.10.240.145 (10.10.240.145:80) nginx-1.14.0.tar.gz 100% |********************************| 992k 0:00:00 ETA Removing intermediate container 3b8cb3885487 ---> 13e679e9c193 Step 12/12 : EXPOSE 80/tcp ---> Running in dd7199f76aa3 Removing intermediate container dd7199f76aa3 ---> 5b194defe89f Successfully built 5b194defe89f Successfully tagged marksugar/httpd:8而后启动,ls /usr/local/nginx,那说明run的命令是执行成功的。nginx解压后是nginx-1.14.0已经被重命名成nginx[root@linuxEA_10_10_240_145 /data/linuxea]$ docker run --name linuxea1 --rm -p 81:80 marksugar/httpd:8 ls /usr/local/nginx CHANGES CHANGES.ru LICENSE README auto conf configure contrib html man src那么RUN是可以被运行成多次的,但是多次意味着被挂在多层,你可以使用一条RUN组织续航起来,否则build后的容器会非常之大的。你需要明白,RUN命令是运行在基础镜像之上,那也就是说RUN命令的运行是随着基础镜像的变化而不同的,如:centos安装软件是yum,ubuntu则是apk-get ,其依赖包名称也是不相同的。
2019年01月11日
2,483 阅读
0 评论
0 点赞
2019-01-10
linuxea:白话容器之使用dockerfile创建简单镜像1(18)
我们在安装一个nginx的时候,通常需要进行定制化模块和配置参数等。在nginx打包时候会使用一些默认的参数来定义并启动。通常我们在使用一个容器的时候,要么进入容器内修改配置文件,而后重载,在没有使用容器的时候,一般我们通过nginx reload加载,但是在容器中,不得已删除在启动。并且在dockerhub上拖一个nginx镜像的时候,不一定是符合我们需要的,我们可以进行修改,而后在commit,将可写层在打包,基于容器的自定义方式,但是这样比nginx relaod更加麻烦了,镜像的体积处理不当也会变的特别大。配置文件打包进容器后,要修改也是一件麻烦事。特别是当在一个环境里面,我们可能多次使用,变更。如:测试,开发,生产等。这样的配置变更是常规的操作,那就意味着需要重新构建多个镜像,这显然是又增加了操作,这样的环境中配置文件写入到容器内在有些时候是不可取的,当然如果有一定的需求,也是可以接受。其实可以自己制作镜像,封装应用程序到镜像中,存储卷将配置文件挂载到相应目录,这样也是可以的。那么在docker是如何解决的?仍然以nginx为例,如果此刻我需要一个虚拟主机,有一个域名,一个端口,一个目录,在nginx配置文件中的conf.d下,我们保存一个文件server.conf,内容如下:{ server_name $NGINX_SERVER_NAME; listen $NGINX_IP:$NGINX_PORT; root $DOC_ROOT }相信你也看到了,以这种变量的方式,在容器内部应用程序启动之前,使用一个程序,这个程序根据镜象中的某个文件,在镜象启动的时候向内部传递变量。这个程序将用户传递的变量替换保存在上述的server.conf文件中,而后由这个程序在启动主进程,而后退出。有一个进程启动另外一个进程并替换当前进程,exec使用配置文件的方式需要一个文件。当然,也可以直接加载系统当前环境变量中的数据,而后替换。这也是其中的表现之一。通常也会有默认的参数,如果不进行变量替换,也会有默认的参数启动。dockerfile此前我们知道,制作镜像的方式有两种,一种基于镜像,一种基于dockerfile,而docker镜像构建之前都已经试过。dockerfile包含制作镜像的代码,包含命令指令,通过指令运行一些命令。dockerfile由两类组成,注释信息和指令(参数)。在dockerfile文件中指令是大写的,但是并不区分dockerfile执行的顺序是从上往下执行的,第一个非注释行必须是FROMdockerfile的目录必须是独有的dockerfile文件首字母必须是大写(Dockerfile)dockerfile中引用的文件,必须在同Dockerfile目录,或者目录下的目录或者文件dockererignoredockererignoredockerfile还支持一个隐藏文件.dockererignore file。在.dockererignore当中可以写入很多文件,支持通配符但凡写入到dockererignore的文件路径,在打包的时候都不会包含进去而后通过docker build命令在dockerfile的独有目录下进行打包,打标签,推送镜像到仓库。相比之前的镜像中使用docker打包可写层,而在基于dockerfile制作镜像的时候,不用启动容器,但是这个过程是docker build来完成。而docker build也是隐藏式的启动一个容器,只不过这个容器不是用我们自己手动去启动而已。而dockerfile中的命令是在dockerfile中FROM的基础镜像之上运行的,如果基础镜像中没有dockerfile中的命令,是无法正常执行的。那么你可能需要进行安装才可以使用。Environment replacementEnvironment replacement之前说的环境变量是使用环境变量来做一些替换,在启动容器时候的环境变量,这里的环境变量是说dockerfile中的环境变量,和docker build有关,并且和shell非常相像,如:ENV NGINXPORT=80引用时候,直接引用,也可以加花括号$NGINXPORT AND ${NGINXPORT}并且还支持shell一样的两种变量替换的特殊格式,如下:${variable:-word} ${variable:+word}${variable:-word}: 这种说明,如果variable未设置或为空,就默认值为word,如果设定就替换设定值${variable:+word}:这种说明,如果variable值不是空,有值的时候就显示为wordFROMFROMFROM指令是最重要的一个且必须是Dockerfile文件开篇的第一个个非注释行,用于为镜像文件构建过程指定的i基础镜像,后续的指令运行与此镜像所提供的运行环境实践中,基准镜像可以是任何可用镜像文件,默认i情况下,docker build会在docker主机上查找指定的镜像文件,在其不存在时,则会从Docker Hub Registry上拉取所需的镜像文件。如果找不到指定的镜像文件,docker build会返回一个错误信息。语法格式: FROM <repository>[:<tag>] FROM <repository>@:<digest> FROM <repository>[:<tag>] 镜象仓库: 标签,这个标签是可以省略的,省略时默认为latest; FROM <repository>@:<digest> 镜象仓库:哈希码MAINTAINERMAINTAINERMAINTAINER用于让Dockerfile制作者提供本人的详细信息,在Dockerfile中MAINTAINER并不限制出现在那个位置,我们建议你放在FROM后面 MAINTANIER "www.linuxea.com"LABELLABEL在较为新的版本上,LABEL替换了MAINTAINER,而LABEL是让用户给镜象指定很多元数据:<key>=<value> <key>=<value> <key>=<value> <key>=<value> <key>=<value> <key>=<value>MAINTAINER的信息可以作为LABEL的一个键值对,并且MAINTAINER在最新版本中是可以兼容的COPYCOPY用于将宿主机的文件复制到创建的新镜象中。在Dockerfile的当前目录中,将某一个文件或者几个文件复制到目标镜象的文件中COPY <src> ... <dest> COPY ["<src>",..."<dest>"]src : 要复制的源文件或者目录,支持使用通配符dest:目标路径,即正在创建的image的文件系统路径,建议为<dest>使用绝对路径,否则,COPY制定则以WORKDIR为起始路径。文件复制准则:<src>必须是build上下文中的路径,不能说其父目录中的文件如果是<src>是目录,则其内部文件或者子目录会被递归复制,但<src>目录自身不会被复制如果指定了多个<src>,或在<src>中使用了通配符,则<dest>必须是一个目录,且必须以/结尾如果<dest>事先不存在,他将会自动创建,这包括其父目录路径我们复制一个文件COPY index.html /data/wwwroot到此为止就已经可以做一个镜象[root@linuxEA_10_10_240_145 /data/linuxea]$ cat Dockerfile # describe : test FROM busybox:latest MAINTAINER www.linuxea.com LABEL maintainer="www.linuxea.com" COPY index.html /data/wwwroot/index.html在当前目录创建一个index.html[root@linuxEA_10_10_240_145 /data/linuxea]$ echo linuxea-`date` >> ./index.html [root@linuxEA_10_10_240_145 /data/linuxea]$ cat ./index.html linuxea-2018年 12月 06日 星期四 19:52:14 CSTbuildbuild的时候,使用-t 命名,并且COPY的文件应该和Dockerfile在同级目录,而后build,如下:[root@linuxEA_10_10_240_145 /data/linuxea]$ docker build -t marksugar/httpd:1 ./ Sending build context to Docker daemon 3.072kB Step 1/4 : FROM busybox:latest ---> 59788edf1f3e Step 2/4 : MAINTAINER www.linuxea.com ---> Using cache ---> 7f12e197dfba Step 3/4 : LABEL maintainer="www.linuxea.com" ---> Using cache ---> c28e9c23ae2f Step 4/4 : COPY index.html /data/wwwroot/index.html ---> f51ffe499c66 Successfully built f51ffe499c66 Successfully tagged marksugar/httpd:1当进行4步后,已经打包完成[root@linuxEA_10_10_240_145 /data/linuxea]$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE marksugar/httpd 1 f30285688fca 2 minutes ago 1.15MB而后我们查看下刚才创建的文件是否已经被打包到/data/wwwroot/index.html中使用cat /data/wwwroot/index.html,如下[root@linuxEA_10_10_240_145 /data/linuxea]$ docker run --name linuxea1 --rm marksugar/httpd:1 cat /data/wwwroot/index.html WARNING: IPv4 forwarding is disabled. Networking will not work. linuxea-2018年 12月 06日 星期四 19:52:14 CST如上,已经被成功打包。那如果我要COPY目录的话,就需要这样来写,如,copy /etc/zabbix/目录到容器中,如下:先复制目录到当前目录下,也就是和Dockerfile同级目录[root@linuxEA_10_10_240_145 /data/linuxea]$ cp -r /etc/zabbix/ ./Dockerfile如下:[root@linuxEA_10_10_240_145 /data/linuxea]$ cat Dockerfile # describe : test FROM busybox:latest MAINTAINER www.linuxea.com LABEL maintainer="www.linuxea.com" COPY index.html /data/wwwroot/index.html COPY zabbix /data/wwwroot/zabbix/而后build[root@linuxEA_10_10_240_145 /data/linuxea]$ docker build -t marksugar/httpd:2 ./ Sending build context to Docker daemon 33.28kB Step 1/5 : FROM busybox:latest ---> 59788edf1f3e Step 2/5 : MAINTAINER www.linuxea.com ---> Using cache ---> 7f12e197dfba Step 3/5 : LABEL maintainer="www.linuxea.com" ---> Using cache ---> c28e9c23ae2f Step 4/5 : COPY index.html /data/wwwroot/index.html ---> Using cache ---> f51ffe499c66 Step 5/5 : COPY zabbix /data/wwwroot/zabbix/ ---> eb31fdcfcd13 Successfully built eb31fdcfcd13 Successfully tagged marksugar/httpd:2验证/data/wwwroot/zabbix目录下的文件是否存在[root@linuxEA_10_10_240_145 /data/linuxea]$ docker run --name linuxea1 --rm marksugar/httpd:2 ls /data/wwwroot/zabbix WARNING: IPv4 forwarding is disabled. Networking will not work. scripts zabbix_agentd.conf zabbix_agentd.d
2019年01月10日
2,614 阅读
0 评论
0 点赞
2019-01-09
linuxea:白话容器之docker存储卷使用的几种方式(17)
docker存储卷docker有两种类型的存储卷,每种类型都在容器中存在一个挂载点,但其在宿主机上的位置有所不同绑定挂在卷容器内和宿主机都需要指定一个已知的路径,二者建立关联关系docker管理卷在容器内指定挂载点,而被绑定到宿主机的目录是不需要已知的,由容器引擎执行创建管理,和容器建立关联关系。一般情况通常为目录下的id号在docker中使用存储卷,使用-v指定目录即可docker管理卷使用-v指定目录即可-v /data/wwwroot,这段路径在容器内,而宿主机是有docker管理的卷[root@linuxEA10_10_240_145 ~]$ docker run --name linuxea -it --network linuxea0 --rm -v /data/wwwroot busybox WARNING: IPv4 forwarding is disabled. Networking will not work. / # ls data/wwwroot/ / #而后可以通过docker inspect linuxea来查看被挂载到宿主机的目录[root@linuxEA10_10_240_145 ~]$ docker inspect -f {{.Mounts}} linuxea [{volume cc94787f8024ffbc381d14098a8b23dca4a4a819287dc996fe320d86c7358c2a /var/lib/docker/volumes/cc94787f8024ffbc381d14098a8b23dca4a4a819287dc996fe320d86c7358c2a/_data /data/wwwroot local true }]也可以看所有的信息[root@linuxEA10_10_240_145 ~]$ docker inspect linuxea ... "Mounts": [ { "Type": "volume", "Name": "a4bfafafc7dcbad92c4fc313850cfbbf056c3c9054cade231ef47a03eccb3dc4", "Source": "/var/lib/docker/volumes/a4bfafafc7dcbad92c4fc313850cfbbf056c3c9054cade231ef47a03eccb3dc4/_data", "Destination": "/data/wwwroot", "Driver": "local", "Mode": "", "RW": true, "Propagation": "" } ... 现在被挂载到/var/lib/docker/volumes/a4bfafafc7dcbad92c4fc313850cfbbf056c3c9054cade231ef47a03eccb3dc4/_data下我们在容器内写入文件/ # echo `date` >> /data/wwwroot/index.html / # cat /data/wwwroot/index.html Tue Dec 4 07:17:17 UTC 2018 / # 而后在查看宿主机/var/lib/docker/volumes/a4bfafafc7dcbad92c4fc313850cfbbf056c3c9054cade231ef47a03eccb3dc4/_data目录回到宿主机[root@linuxEA10_10_240_145 ~]$ ll /var/lib/docker/volumes/a4bfafafc7dcbad92c4fc313850cfbbf056c3c9054cade231ef47a03eccb3dc4/_data 总用量 4 -rw-r--r-- 1 root root 28 12月 4 15:17 index.html [root@linuxEA10_10_240_145 ~]$ cat /var/lib/docker/volumes/a4bfafafc7dcbad92c4fc313850cfbbf056c3c9054cade231ef47a03eccb3dc4/_data/index.html Tue Dec 4 07:17:17 UTC 2018在宿主机追加到文件中,而后在返回容器查看是否在容器中也有[root@linuxEA10_10_240_145 ~]$ echo `date` >> /var/lib/docker/volumes/a4bfafafc7dcbad92c4fc313850cfbbf056c3c9054cade231ef47a03eccb3dc4/_data/index.html [root@linuxEA10_10_240_145 ~]$ cat /var/lib/docker/volumes/a4bfafafc7dcbad92c4fc313850cfbbf056c3c9054cade231ef47a03eccb3dc4/_data/index.html Tue Dec 4 07:17:17 UTC 2018 2018年 12月 04日 星期二 15:34:58 CST容器中/ # cat /data/wwwroot/index.html Tue Dec 4 07:17:17 UTC 2018 2018年 12月 04日 星期二 15:34:58 CST绑定挂载卷如:-v /data/wwwrootdir:/data/wwwroot 。这里的/data/wwwrootdir:/data/wwwroot,前面的/data/wwwrootdir是宿主机的目录,后面的/data/wwwroot是容器内的目录如果目录不存在则创建,如果存在则覆盖[root@linuxEA10_10_240_145 ~]$ docker run --name linuxea -it --network linuxea0 --rm -v /data/wwwrootdir:/data/wwwroot busybox WARNING: IPv4 forwarding is disabled. Networking will not work. / # 此时在宿主机上验证一下。在echo `date +%D-%T` >> /data/wwwrootdir/index.html写入时间[root@linuxEA10_10_240_145 ~]$ echo `date +%D-%T` >> /data/wwwrootdir/index.html [root@linuxEA10_10_240_145 ~]$ cat /data/wwwrootdir/index.html 12/04/18-15:41:24回到容器内,index.html文件中已经注入的信息和宿主机一样。/ # cat /data/wwwroot/index.html 12/04/18-15:41:24而后删除容器,那这个文件仍然存在。[root@linuxEA10_10_240_145 ~]$ ll /data/wwwrootdir/index.html -rw-r--r-- 1 root root 18 12月 4 15:41 /data/wwwrootdir/index.html [root@linuxEA10_10_240_145 ~]$ cat /data/wwwrootdir/index.html 12/04/18-15:41:24在重新run容器,挂载相同的目录[root@linuxEA10_10_240_145 ~]$ docker run --name linuxea -it --network linuxea0 --rm -v /data/wwwrootdir:/data/wwwroot busybox WARNING: IPv4 forwarding is disabled. Networking will not work. / # cat /data/wwwroot/index.html 12/04/18-15:41:24[root@linuxEA10_10_240_145 ~]$ docker inspect -f {{.Mounts}} linuxea [{bind /data/wwwrootdir /data/wwwroot true rprivate}]可以看见文件仍然存在。由此,绑定挂载可完成一定意义的持久存储。共享存储卷两个容器分别是linuxea和linuxea1挂载宿主机同一个目录,并且在容器内写入数据共享linuxea[root@linuxEA10_10_240_145 ~]$ docker run --name linuxea -it --network linuxea0 --rm -v /data/wwwrootdir:/data/wwwroot busybox WARNING: IPv4 forwarding is disabled. Networking will not work. / # echo "linuxea" >> /data/wwwroot/index.html linuxea1[root@linuxEA10_10_240_145 ~]$ docker run --name linuxea1 -it --network linuxea0 --rm -v /data/wwwrootdir:/data/wwwroot busybox WARNING: IPv4 forwarding is disabled. Networking will not work. / # cat /data/wwwroot/index.html 12/04/18-15:41:24 linuxea / # echo "linuxea1" >> /data/wwwroot/index.html / # cat . cat: read error: Is a directory / # cat /data/wwwroot/index.html 12/04/18-15:41:24 linuxea linuxea1回到linuxea/ # cat /data/wwwroot/index.html 12/04/18-15:41:24 linuxea linuxea1复制存储卷现在我们知道docker可以共享多个容器存储卷,但是如果共享太多操作起来会较为麻烦,我们可以使用复制来做和上面一样,在初始化run的时候,指明要复制那个卷,使用--volumes-from选项。如下[root@linuxEA10_10_240_145 ~]$ docker run --name linuxea2 -it --network linuxea0 --rm --volumes-from linuxea busybox WARNING: IPv4 forwarding is disabled. Networking will not work. / # cat /data/wwwroot/index.html 12/04/18-15:41:24 linuxea linuxea1这样一来就有了三个容器使用同一个存储卷。联盟容器之存储卷那这样的话就可以延续之前网络中的的“联盟式容器”我们创建一个容器,只要存在就可以,并且指定存储卷路径,而后这个容器作为后面启动其他几个有关联容器的基础架构容器。nginx容器复制存储卷设置,tomcat也复制存储卷设置。“联盟式容器”则会共享网络名称空间,就可以建立密集的联盟关系。这样nginx和tomcat使用底层容器创建的网络名称空间,都通过底层容器的一个对外的网络ip和lo接口,lo用作内部通讯tomcat可监听,nginx监听对外的网络地址。用户先到nginx外部ip,而后到内部lo tomcat我们简单使用联盟容器和volumes-from创建一个容器,作为基础容器.--network linuxea0是我自己创建的linuxea0,可以不指定[root@linuxEA10_10_240_145 ~]$ docker run --name init -it --network linuxea0 -v /data/init/volume:/data/wwwroot busybox WARNING: IPv4 forwarding is disabled. Networking will not work. / # 而后创建一个nginx加入到init网络中,并且复制init的存储卷--volumes-from[root@linuxEA10_10_240_145 ~]$ docker run --name httpd --network container:init --volumes-from init -it busybox WARNING: IPv4 forwarding is disabled. Networking will not work. / # 我们来查看下存储卷文件路径[root@linuxEA10_10_240_145 ~]$ docker inspect -f {{.Mounts}} httpd [{bind /data/init/volume /data/wwwroot true rprivate[root@linuxEA10_10_240_145 ~]$ docker inspect -f {{.Mounts}} init [{bind /data/init/volume /data/wwwroot true rprivate}]和ip[root@linuxEA10_10_240_145 ~]$ docker inspect -f {{.NetworkSettings.Networks.linuxea0.IPAddress}} init 172.25.100.2而httpd的ip地址是加入到init的,inspect看不到,我们通过进入容器ip a查看[root@linuxEA10_10_240_145 ~]$ docker run --name httpd --network container:init --volumes-from init -it busybox WARNING: IPv4 forwarding is disabled. Networking will not work. / # ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 135: eth0@if136: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether 02:42:ac:19:64:02 brd ff:ff:ff:ff:ff:ff inet 172.25.100.2/24 brd 172.25.100.255 scope global eth0 valid_lft forever preferred_lft forever / # 这样配置tomcat绑定127.0.0.1端口,而后使用httpd或者nginx代理本地127.0.0.1的tomcat端口即可。
2019年01月09日
2,679 阅读
0 评论
0 点赞
1
2
3