首页
About Me
Search
1
linuxea:gitlab-ci之docker镜像质量品质报告
49,484 阅读
2
linuxea:如何复现查看docker run参数命令
23,648 阅读
3
Graylog收集文件日志实例
18,633 阅读
4
linuxea:jenkins+pipeline+gitlab+ansible快速安装配置(1)
18,423 阅读
5
git+jenkins发布和回滚示例
18,235 阅读
ops
Openppn
Sys Basics
rsync
Mail
NFS
Other
Network
HeartBeat
server 08
Code
Awk
Shell
Python
Golang
vue
virtualization
KVM
Docker
openstack
Xen
kubernetes
kubernetes-cni
Service Mesh
Data
Mariadb
PostgreSQL
MongoDB
Redis
MQ
Ceph
TimescaleDB
kafka
surveillance system
zabbix
ELK Stack/logs
Open-Falcon
Prometheus
victoriaMetrics
Web
apache
Tomcat
Nginx
自动化
Puppet
Ansible
saltstack
Proxy
HAproxy
Lvs
varnish
更多
互联咨询
最后的净土
软件交付
持续集成
gitops
devops
登录
Search
标签搜索
kubernetes
docker
zabbix
Golang
mariadb
持续集成工具
白话容器
elk
linux基础
nginx
dockerfile
Gitlab-ci/cd
最后的净土
基础命令
gitops
jenkins
docker-compose
Istio
haproxy
saltstack
marksugar
累计撰写
667
篇文章
累计收到
111
条评论
首页
栏目
ops
Openppn
Sys Basics
rsync
Mail
NFS
Other
Network
HeartBeat
server 08
Code
Awk
Shell
Python
Golang
vue
virtualization
KVM
Docker
openstack
Xen
kubernetes
kubernetes-cni
Service Mesh
Data
Mariadb
PostgreSQL
MongoDB
Redis
MQ
Ceph
TimescaleDB
kafka
surveillance system
zabbix
ELK Stack/logs
Open-Falcon
Prometheus
victoriaMetrics
Web
apache
Tomcat
Nginx
自动化
Puppet
Ansible
saltstack
Proxy
HAproxy
Lvs
varnish
更多
互联咨询
最后的净土
软件交付
持续集成
gitops
devops
页面
About Me
搜索到
91
篇与
的结果
2019-03-04
linuxea:尽可能不在docker镜像中嵌入配置或者密码
切勿将配置或机密信息嵌入Docker镜像中。相反,在构建Docker镜像时,期望使用业务流程运行时将配置和机密的信息提供给容器,这些包含:Kubernetes Secrets,Docker Secrets。而外部工具或环境变量(对于非敏感性)用于非敏感数据。但仍需注意,不要无意中在镜像层的隐藏层中包含机密信息。概述容器镜像应该是可重用且安全的。当镜像层包含嵌入式配置或机密信息时,它违反了此规则,并且不可重用或不安全。这些值不属于镜像,它们仅属于正在运行的容器中。当凭据保留在Docker镜像中时,可能会出现未知的安全隐患。在生产中运行Docker容器应该是具有各种配置和密码信息的镜像的组合。如果你在Docker,Kubernetes,Swarm或其他业务流程层中运行此容器并不重要,编排平台应负责通过将这些部件组装到正在运行的容器中来创建容器。如我上述所提到的,secrets,configmap等。Docker镜像的类型Component imageDocker镜像通常是可重用的组件,可与不同的基础架构共享,并可在各种环境中运行。这种类型的image的一个例子是Postgres,Redis或NSQ。这些组件通常需要一些凭证来引导。凭证应由容器提供,而不是内置于镜像中。官方MySQL镜像就是一个很好的例子。如果你运行MySQL Docker镜像而不为某些配置提供初始值,则容器将退出:[root@linuxea.com ~]# docker run mysql error: database is uninitialized and password option is not specified You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORDMySQL镜像可以很好地显示缺少的值并给出建议,查看docker-entrpoint.sh脚本。再试一次,键入密码:MYSQL_ROOT_PASSWORD=www.linuxea.com[root@linuxea.com ~]# docker run -e MYSQL_ROOT_PASSWORD=www.linuxea.com mysql Initializing database 2019-03-04T07:53:38.390516Z 0 [Warning] [MY-011070] [Server] 'Disabling symbolic links ...... Warning: Unable to load '/usr/share/zoneinfo/zone1970.tab' as time zone. Skipping it. 2019-03-04T07:54:19.457699Z 0 [System] [MY-010910] [Server] /usr/sbin/mysqld: Shutdown complete (mysqld 8.0.15) MySQL Community Server - GPL. MySQL init process done. Ready for start up. 2019-03-04T07:54:19.847135Z 0 [Warning] [MY-011070] [Server] 'Disabling symbolic links using --skip-symbolic-links (or equivalent) is the default. Consider not using this option as it' is deprecated and will be removed in a future release. .... 2019-03-04T07:54:20.734608Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.15' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server - GPL. 2019-03-04T07:54:20.756534Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Socket: '/var/run/mysqld/mysqlx.sock' bind-address: '0.0.0.0' port: 33060这一次,提供了一些配置选项,它可以工作。MySQL镜像是可重用且安全的,因为它可以配置为在大多数环境中工作,并且没有嵌入到镜像中的密码。Application image镜像可以包含应用程序。应用程序镜像的示例是静态站点或API。这些镜像旨在为用户而非其他组件提供请求。通常,这些应用程序镜像需要认证,例如API密钥或数据库凭证,或者这些需要特定于环境的数据,例如要连接的其他端点。除了dev模式默认值之外,在镜像本身中存储这些认证是一种不好的做法,应该避免使用。认证信息应始终由容器提供,而不是内置于镜像中。如:ELK的前段是Kibana,Kibana容器需要一些配置, 至少它必须能够找到要连接的Elasticsearch服务器。elastic.co团队认为默认链接端口9200上名为elasticsearch的主机,并理所当然的为此配置了默认密码,因为这在docker-compose(dev)环境中运行良好。但希望没有人使用用户名“elastic”和密码“changeme”在生产中运行Elasticsearch集群!隐藏层最后,重要的是不要在镜像的隐藏层中包含机密的信息。如果Dockerfile配置不正确,则在创建镜像时可能会发生这种情况。当删除Dockerfile中的内容时(秘密,文件,源代码,等),并不总是意味着这个密码没有被包含在最终图像中。它可能存在于父层中,但仍可访问。当用户运行docker pull命令时,将传递所有基础层。FROM marksugar:nginx RUN git clone https://USERNAME:PASSWORD@git.linuxea.com -o /usr/local/www WORKDIR /usr/local/www RUN go build -o /data/wwwroot/ RUN rm -rf /usr/local/www ENTRYPOINT [/data/wwwroot]这个Dockerfile的开发人员希望在Docker构建过程中创建一个特殊的构建环境。在构建时,需要将源代码添加到image中,构建二进制文件然后删除源代码。问题是Dockerfile中的每一行都会生成一个新层,每当拉出最终镜像时候时都会pull该层。创建此Dockerfile的过程也会将源代码泄漏给任何提取此镜像的人。为什么这些很重要?Docker镜像应该功能齐全,一致,可重用(可共享和可发现),小巧,安全,可在企业环境中工作并可调试可重复使用应用程序可移植性是部署容器的最大好处之一。当应用程序组件可移植时,可以更轻松地跨各种云提供程序和环境部署不可变基础结构。具有内置配置和包含机密的信息的容器不可移植,它们可能需要为每个环境提供不同的镜像。安全嵌入密码的镜像不安全。任何有权访问镜像的人都可以拉动和查看密码信息,即使它们处于不可见面图层中。这是未经审计的密码信息查看方式,应该避免。企业兼容一个好的的容器镜像包含运行组件时的所有最佳实践。从容器镜像中删除配置将允许企业用户从容器中包含的最佳实践中受益,并且仍然与其审计和安全策略及其工作流程兼容。内置配置将要求企业使用创建私有镜像并失去原始镜像中已有的最佳实践的大部分好处。可调试具有嵌入式配置的镜像调试起来要困难得多。如果镜像具有嵌入式配置,则通过简单docker run命令从镜像启动可调试容器通常更加困难。建议有许多方法可以在运行时为容器提供机密的信息和配置:环境变量配置文件etcd或其他配置管理服务Docker的secrets个config(依赖于swarm)外部secret管理解决方案即使构建的image仅在内部使用且仅在单个服务器上使用,也应该使用一种更好的方式创建镜像。切勿在镜像中提供默认的硬密码,也不要添加其他的机密信息,而是在运行容器时为它们提供。而这些方式参考以上5种,而最常见的是环境变量和配置文件。更多阅读有关如何将密码信息传递到容器的更多信息的一些建议是:linuxea:docker config配置使用linuxea:变量实值与文件变量替换linuxea:compose中的变量传递与docker-createrepo构建linuxea:kubernetes secret简单用法(25)linuxea:kubernetes 介绍ConfigMap与Secret(23)学习更多学习如何使用Docker CLI命令,Dockerfile命令,使用这些命令可以帮助你更有效地使用Docker应用程序。查看Docker文档和我的其他帖子以了解更多信息。docker目录白话容器docker-compose
2019年03月04日
3,920 阅读
0 评论
0 点赞
2019-03-02
linuxea:docker config的配置使用
使用Docker Config而不是使用嵌入式配置创建镜像怎么样?在镜像中嵌入配置?我们经常看到Dockerfile如下所示,其中创建新镜像只是为了将配置添加到基本镜像。$ cat Dockerfile FROM marksugar:redis:5.0.0 RUN curl http:/xx/x/xredis.conf -o /etc/redis/redis.conf在上述中,是将最新的redis.conf文件放到/etc/redis/redis.conf中除此之外,我们还可以使用变量替换的方式进行处理。变量替换意味着需要设计模板和参数,如果参数过多,配置和替换起来将会耗费更多的时间,也较为复杂,并不是最好最安全的方式。当然,我们可以直接挂载文件到容器中,那并不优雅,但的确能解决一部分问题。如果需要频繁的修改文件,且变量替换也无法满足,也不想挂载文件,那现在就可以参考本节的docker config。阅读本章,你将了解docker config的简单使用和docker swarm的简单使用。使用场景有一个配置redis.conf的配置文件在容器内,现在我将容器内的配置文件修改后重新附加到容器内。我修改了密码字段:masterauth "OTdmOWI4ZTM4NTY1M2M4OTZh.com" requirepass "OTdmOWI4ZTM4NTY1M2M4OTZh.com"config创建现在,使用Docker CLI,我们可以从此配置文件创建配置redis.conf(redis.conf文件此前准备好的配置),我们将此配置命名为redis。[root@linuxea.com /opt/2019/redis]# docker config create redis redis.conf 269yklanwzva9yk74n6b3j9dk然后我们可以检查配置,就像我们对其他任何Docker一样:[root@linuxea.com /opt/2019/redis]# docker config inspect redis [ { "ID": "269yklanwzva9yk74n6b3j9dk", "Version": { "Index": 24 }, "CreatedAt": "2019-03-02T02:36:19.257840639Z", "UpdatedAt": "2019-03-02T02:36:19.257840639Z", "Spec": { "Name": "redis", "Labels": {}, "Data": "YmluZCAwLjAuMC4wDQpwcm90ZWN0ZWQtbW9kZSB5ZXMNCnBvcnQgNjM3OQ0KdGNwLWJhY2tsb2cgNTExDQp0aW1lb3V0IDANCnRjcC1rZWVwYWxpdmUgMzAwDQpkYWVtb25pemUgbm8NCnN1cGVydmlzZWQgbm8NCnBpZGZpbGUgL3Zhci9ydW4vcmVkaXNfNjM3OS5waWQNCmxvZ2xldmVsIG5vdGljZQ0KbG9nZmlsZSAiL2RhdGEvbG9ncy9yZWRpc182Mzc5LmxvZyINCmRhdGFiYXNlcyAxNg0KYWx3YXlzLXNob3ctbG9nbyB5ZXMNCnNhdmUgOTAwIDENCnNhdmUgMzAwIDEwDQpzYXZlIDYwIDEwMDAwDQpzdG9wLXdyaXRlcy1vbi1iZ3NhdmUtZXJyb3IgeWVzDQpyZGJjb21wcmVzc2lvbiB5ZXMNCnJkYmNoZWNrc3VtIHllcw0KZGJmaWxlbmFtZSBkdW1wLnJkYg0KZGlyICIvZGF0YS9yZWRpcyINCnJlcGxpY2Etc2VydmUtc3RhbGUtZGF0YSB5ZXMNCnJlcGxpY2EtcmVhZC1vbmx5IHllcw0KcmVwbC1kaXNrbGVzcy1zeW5jIG5vDQpyZXBsLWRpc2tsZXNzLXN5bmMtZGVsYXkgNQ0KcmVwbC1kaXNhYmxlLXRjcC1ub2RlbGF5IG5vDQpyZXBsaWNhLXByaW9yaXR5IDEwMA0KbGF6eWZyZWUtbGF6eS1ldmljdGlvbiBubw0KbGF6eWZyZWUtbGF6eS1leHBpcmUgbm8NCmxhenlmcmVlLWxhenktc2VydmVyLWRlbCBubw0KcmVwbGljYS1sYXp5LWZsdXNoIG5vDQphcHBlbmRvbmx5IG5vDQphcHBlbmRmaWxlbmFtZSAiYXBwZW5kb25seS5hb2YiDQphcHBlbmRmc3luYyBldmVyeXNlYw0Kbm8tYXBwZW5kZnN5bmMtb24tcmV3cml0ZSBubw0KYXV0by1hb2YtcmV3cml0ZS1wZXJjZW50YWdlIDEwMA0KYXV0by1hb2YtcmV3cml0ZS1taW4tc2l6ZSA2NG1iDQphb2YtbG9hZC10cnVuY2F0ZWQgeWVzDQphb2YtdXNlLXJkYi1wcmVhbWJsZSB5ZXMNCmx1YS10aW1lLWxpbWl0IDUwMDANCnNsb3dsb2ctbG9nLXNsb3dlci10aGFuIDEwMDAwDQpzbG93bG9nLW1heC1sZW4gMTI4DQpsYXRlbmN5LW1vbml0b3ItdGhyZXNob2xkIDANCm5vdGlmeS1rZXlzcGFjZS1ldmVudHMgIiINCmhhc2gtbWF4LXppcGxpc3QtZW50cmllcyA1MTINCmhhc2gtbWF4LXppcGxpc3QtdmFsdWUgNjQNCmxpc3QtbWF4LXppcGxpc3Qtc2l6ZSAtMg0KbGlzdC1jb21wcmVzcy1kZXB0aCAwDQpzZXQtbWF4LWludHNldC1lbnRyaWVzIDUxMg0KenNldC1tYXgtemlwbGlzdC1lbnRyaWVzIDEyOA0KenNldC1tYXgtemlwbGlzdC12YWx1ZSA2NA0KaGxsLXNwYXJzZS1tYXgtYnl0ZXMgMzAwMA0Kc3RyZWFtLW5vZGUtbWF4LWJ5dGVzIDQwOTYNCnN0cmVhbS1ub2RlLW1heC1lbnRyaWVzIDEwMA0KYWN0aXZlcmVoYXNoaW5nIHllcw0KY2xpZW50LW91dHB1dC1idWZmZXItbGltaXQgbm9ybWFsIDAgMCAwDQpjbGllbnQtb3V0cHV0LWJ1ZmZlci1saW1pdCByZXBsaWNhIDI1Nm1iIDY0bWIgNjANCmNsaWVudC1vdXRwdXQtYnVmZmVyLWxpbWl0IHB1YnN1YiAzMm1iIDhtYiA2MA0KaHogMTANCmR5bmFtaWMtaHogeWVzDQphb2YtcmV3cml0ZS1pbmNyZW1lbnRhbC1mc3luYyB5ZXMNCnJkYi1zYXZlLWluY3JlbWVudGFsLWZzeW5jIHllcw0KbWF4bWVtb3J5LXBvbGljeSBhbGxrZXlzLWxmdQ0KbGZ1LWxvZy1mYWN0b3IgMTANCmxmdS1kZWNheS10aW1lIDENCm1heGNsaWVudHMgNjAwDQptYXhtZW1vcnkgNDA5Nk0NCm1hc3RlcmF1dGggIk9UZG1PV0k0WlRNNE5UWTFNMk00T1RaaC5jb20iDQpyZXF1aXJlcGFzcyAiT1RkbU9XSTRaVE00TlRZMU0yTTRPVFpoLmNvbSINCg==" } } ]数据仅为base64编码,可以轻松解码。[root@linuxea.com /opt/2019/redis]# docker config inspect -f '{{json .Spec.Data}}' redis|cut -d '"' -f2 |base64 -d bind 0.0.0.0 protected-mode yes port 6379 .... masterauth "OTdmOWI4ZTM4NTY1M2M4OTZh.com" requirepass "OTdmOWI4ZTM4NTY1M2M4OTZh.com"我修改了密码字段: OTdmOWI4ZTM4NTY1M2M4OTZh.com根据定义,配置中的数据不是机密的,因此不加密。docker-compose config配置注意:由于配置是在运行应用程序之前创建的,因此在此文件中将其定义为external。如下:version: '3.7' services: redis: image: marksugar/redis:5.0.0 container_name: redis restart: always network_mode: "host" privileged: true environment: - REDIS_CONF=off - REQUIRE_PASS=OTdmOWI4ZTM4NTY1M2M4OTZh - MASTER_AUTH=OTdmOWI4ZTM4NTY1M2M4OTZh - MAXCLIENTS_NUM=600 - MAXMEMORY_SIZE=4096M volumes: - /etc/localtime:/etc/localtime:ro - /data/redis-data:/data/redis:Z - /data/logs:/data/logs configs: - source: redis target: /etc/redis/redis.conf configs: redis: external: true其中configs引用如下:target: 配置文件在容器中的位置configs: 全局调用和局部调用version: '3.7' services: redis: image: .... configs: - source: redis target: /etc/redis/redis.conf configs: redis: external: true config的名称为redis,在全局配置中则填写redis,在局部配置中附加配置即可docker swarm 使用然后可以使用以下命令运行该应用程序:[root@linuxea.com /opt/2019/redis]# docker stack deploy -c ./docker-compose.yaml redis Ignoring unsupported options: network_mode, privileged, restart Ignoring deprecated options: container_name: Setting the container name is not supported. Creating network redis_default Creating service redis_redis验证[root@linuxea.com /opt/2019/redis]# docker exec -it redis_redis.1.s6f35kwutld2d7oc80e4531rp cat /etc/redis/redis.conf bind 0.0.0.0 protected-mode yes port 6379 ... masterauth "OTdmOWI4ZTM4NTY1M2M4OTZh.com" requirepass "OTdmOWI4ZTM4NTY1M2M4OTZh.com" ...info信息[root@linuxea.com /opt/2019/redis]# docker exec -i redis_redis.1.s6f35kwutld2d7oc80e4531rp redis-cli -a OTdmOWI4ZTM4NTY1M2M4OTZh.com info|grep cpu Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. used_cpu_sys:1.445721 used_cpu_user:1.499864 used_cpu_sys_children:0.000000 used_cpu_user_children:0.000000删除命令[root@linuxea.com /opt/2019/redis]# docker stack rm redis Removing service redis_redis Removing network redis_defaultupdate当需要修改配置的内容时,创建新配置(使用docker config create)然后更新服务以删除先前配置的config,并添加对新配置是一种常见模式。服务命令--config-rm和--config-add是我们修改配置文件的密码,而后重新创建一个redis1的config文件masterauth "OTdmOWI4ZTM4NTY1M2M4OTZh.com" requirepass "OTdmOWI4ZTM4NTY1M2M4OTZh.com.cn"创建redis1的config[root@linuxea.com opt/2019/redis]# docker config create redis1 redis.conf a4soto5rw4if4h7ir8jfrasyv更新配置文件先删除掉redis的config,而后添加redis1,这个过程会更新redis容器[root@linuxea.com /opt/2019/redis]# docker service update --config-rm redis --config-add src=redis1,target=/etc/redis/redis.conf redis_redis redis_redis overall progress: 1 out of 1 tasks 1/1: running [==================================================>] verify: Service converged 查看更新[root@linuxea.com /opt/2019/redis]# docker exec -it redis_redis.1.5qpc63nvm75w03v3ihk17hgie cat /etc/redis/redis.conf bind 0.0.0.0 protected-mode yes .... masterauth "OTdmOWI4ZTM4NTY1M2M4OTZh.com" requirepass "OTdmOWI4ZTM4NTY1M2M4OTZh.com.cn"
2019年03月02日
7,402 阅读
0 评论
0 点赞
2019-02-27
linuxea:缩减docker镜像大小的5个步骤
docker容器的应用程序易于部署管理是基于docker镜像,一个优良的docker镜像是非常有必要的。在多数情况下,我们处于无状态应用的快速部署,这个过程中,且不管你在dockerhub使用还是本地镜像仓库使用,合理的镜像的大小也是有必要的。除此之外,我们应该考虑那些?Docker镜像大小的重要性?网络带宽导致的空闲等待除了镜像大小外,我们还需要考虑更新迭代的问题,更大的基础镜像意味着需要更多的开发时间。这里必然少不了网络带宽的问题,上传下载的等待时间问题。磁盘空间问题当然,镜像大小不单单影响了上传下载操作人员等待的时间。如果你留意过磁盘的使用,你会发现磁盘空间也是一个非常吃紧的事情。安全问题事实上,磁盘并不会耗费太多的金钱。在内网来讲,带宽也不是一个特别大的问题。更多的需要注意的是容器的安全性的问题。太大的镜像往往意味着有更大的攻击面缩减镜像5个步骤在开始之前,我想复述一个重要的问题:你应该妥善考虑将要部署的应用的语言,框架,扩展,工具和第三方包。因为所有安装的内容都会增加镜像大小,docker镜像只需要运行应用所需的环境足以,其他的能少尽量的减少。安装适合的依赖工具包对于从0构建来说,生产的镜像仅仅配置有必要的程序以来包,对于一些不必要的包应该全部删除。如:curl,wget等等。更小的基础镜像假如你要从0开始构建,通常,常见的基础镜像是ubuntu和centos以及debian,除此之外,我们可以考虑使用alpine,alpine的大小仅仅只有5M。使用alpine减小了镜像的大小,但是alpine和centos,debian有一定的差别如果你之是运行一个python程序,或者php程序,你应该考虑使用官方的镜像,比如 : ptyhon:2.7-alpine3.8,这仅仅几十兆。你可以进行自己构建,假如你掌握了这些知识更少的层docker的层也决定了镜像的大小,我们应该尽可能的使用更少的层来构建。更少的层可能意味着更少的dockerfile命令,比如run命令,阅读dockerfile中的RUN指令对镜像大小的影响有助于你深入了解。同时,在很多时候,更少的层解决不了过于繁琐复杂的镜像构建,这时候就需要多阶段构建来解决。使用.dockerignore文件.dockerignore在构建的时候,控制构建的上下文中的内容,对一些内容进行忽略,从而达到想要的预期效果。对于一些特别不需要的文件这特别有用,你可以参考linuxea:docker不能忽视的.dockerignore用法深入了解压缩镜像docker-squash最后一个方式是压缩镜像,docker-squash是一个实用程序,用于将多个层压缩成一个,以便创建具有更少和更小层的镜像。它保留了Dockerfile命令,如PORT,ENV等。这样压扁镜像的工作方式与最初构建时相同。此外,后续图层中删除的文件实际上是在压缩时从镜像中清除的。延伸阅读linuxea:docker不能忽视的.dockerignore用法linuxea:docker多阶段构建Multi-Stage与Builder对比总结docker-squashlinuxea:dockerfile中的RUN指令对镜像大小的影响学习更多学习如何使用Docker CLI命令,Dockerfile命令,使用这些命令可以帮助你更有效地使用Docker应用程序。查看Docker文档和我的其他帖子以了解更多信息。docker目录白话容器docker-compose
2019年02月27日
3,513 阅读
0 评论
0 点赞
2019-02-26
linuxea:dockerfile中的RUN指令对镜像大小的影响
我们在github上,或者在一些应用官方提供的docker镜像的Dockerfile中,经常会看到很多难以琢磨的操作,这篇文章主要说明使用Dockerfile的RUN命令为什么要在后面使用&&链接,以及在实际镜像中的影响和区别。阅读本篇文章对Dockerfile的RUN命令和层有更深的认识,同时学会更好的缩减容器的镜像。为了更好的演示RUN命令与层带来的差异,我们分为几个步骤来做!1,测试多条命令的大小与差异2,减少多条命令的大小与差异多条RUN首先,我们进行多条命令RUN命令测试,如下:安装nginx,php,curl三个软件包[root@linuxea.com ~]# cat Dockerfile FROM alpine:3.9 as default MAINTAINER www.linuxea.com LABEL maintainer="www.linuxea.com" RUN apk add nginx RUN apk add php RUN apk add curl在Build的同时,依次进行[root@linuxea.com ~]# docker build -t linuxea:26 . Sending build context to Docker daemon 427kB Step 1/6 : FROM alpine:3.9 as default ---> caf27325b298 Step 2/6 : MAINTAINER www.linuxea.com ---> Using cache ---> 5ee27f5e579a Step 3/6 : LABEL maintainer="www.linuxea.com" ---> Using cache ---> a9b2f826f6c9 Step 4/6 : RUN apk add nginx ---> Running in c2b048d78656 fetch https://dl-cdn.alpinelinux.org/alpine/v3.9/main/x86_64/APKINDEX.tar.gz fetch https://dl-cdn.alpinelinux.org/alpine/v3.9/community/x86_64/APKINDEX.tar.gz (1/2) Installing pcre (8.42-r1) (2/2) Installing nginx (1.14.2-r0) Executing nginx-1.14.2-r0.pre-install Executing busybox-1.29.3-r10.trigger OK: 7 MiB in 16 packages Removing intermediate container c2b048d78656 ---> 116c307055ed Step 5/6 : RUN apk add php ---> Running in 29c651203043 (1/7) Installing php7-common (7.2.14-r0) (2/7) Installing ncurses-terminfo-base (6.1_p20190105-r0) (3/7) Installing ncurses-terminfo (6.1_p20190105-r0) (4/7) Installing ncurses-libs (6.1_p20190105-r0) (5/7) Installing libedit (20181209.3.1-r0) (6/7) Installing libxml2 (2.9.9-r1) (7/7) Installing php7 (7.2.14-r0) Executing busybox-1.29.3-r10.trigger OK: 21 MiB in 23 packages Removing intermediate container 29c651203043 ---> ec1cbef7f89e Step 6/6 : RUN apk add curl ---> Running in d96aef6e09f8 (1/5) Installing ca-certificates (20190108-r0) (2/5) Installing nghttp2-libs (1.35.1-r0) (3/5) Installing libssh2 (1.8.0-r4) (4/5) Installing libcurl (7.63.0-r0) (5/5) Installing curl (7.63.0-r0) Executing busybox-1.29.3-r10.trigger Executing ca-certificates-20190108-r0.trigger OK: 22 MiB in 28 packages Removing intermediate container d96aef6e09f8 ---> f4000a9883f1 Successfully built f4000a9883f1 Successfully tagged linuxea:26此时构建的镜像大小为18.8M[root@linuxea.com ~]# docker images linuxea:26 REPOSITORY TAG IMAGE ID CREATED SIZE linuxea 26 f4000a9883f1 12 seconds ago 18.8MB分为七层,每一条命令都分为一层,加起来的总大小是18.8M[root@linuxea.com ~]# docker history linuxea:26 IMAGE CREATED CREATED BY SIZE COMMENT f4000a9883f1 18 seconds ago /bin/sh -c apk add curl 1.68MB ec1cbef7f89e 21 seconds ago /bin/sh -c apk add php 8.89MB 116c307055ed 54 seconds ago /bin/sh -c apk add nginx 2.74MB a9b2f826f6c9 9 days ago /bin/sh -c #(nop) LABEL maintainer=www.linu… 0B 5ee27f5e579a 9 days ago /bin/sh -c #(nop) MAINTAINER www.linuxea.com 0B caf27325b298 3 weeks ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B <missing> 3 weeks ago /bin/sh -c #(nop) ADD file:2a1fc9351afe35698… 5.53MB 单条RUN此刻,我们尝试将安装的nginx,php,curl放在一条RUN,使用&&链接起来执行[root@linuxea.com ~]# cat Dockerfile FROM alpine:3.9 as default MAINTAINER www.linuxea.com LABEL maintainer="www.linuxea.com" RUN apk add nginx \ && apk add php \ && apk add curl [root@linuxea.com ~]# docker build -t linuxea:26-1 . Sending build context to Docker daemon 427kB Step 1/4 : FROM alpine:3.9 as default ---> caf27325b298 Step 2/4 : MAINTAINER www.linuxea.com ---> Using cache ---> 5ee27f5e579a Step 3/4 : LABEL maintainer="www.linuxea.com" ---> Using cache ---> a9b2f826f6c9 Step 4/4 : RUN apk add nginx && apk add php && apk add curl ---> Running in 19fb009c38d1 fetch https://dl-cdn.alpinelinux.org/alpine/v3.9/main/x86_64/APKINDEX.tar.gz fetch https://dl-cdn.alpinelinux.org/alpine/v3.9/community/x86_64/APKINDEX.tar.gz (1/2) Installing pcre (8.42-r1) (2/2) Installing nginx (1.14.2-r0) Executing nginx-1.14.2-r0.pre-install Executing busybox-1.29.3-r10.trigger OK: 7 MiB in 16 packages (1/7) Installing php7-common (7.2.14-r0) (2/7) Installing ncurses-terminfo-base (6.1_p20190105-r0) (3/7) Installing ncurses-terminfo (6.1_p20190105-r0) (4/7) Installing ncurses-libs (6.1_p20190105-r0) (5/7) Installing libedit (20181209.3.1-r0) (6/7) Installing libxml2 (2.9.9-r1) (7/7) Installing php7 (7.2.14-r0) Executing busybox-1.29.3-r10.trigger OK: 21 MiB in 23 packages (1/5) Installing ca-certificates (20190108-r0) (2/5) Installing nghttp2-libs (1.35.1-r0) (3/5) Installing libssh2 (1.8.0-r4) (4/5) Installing libcurl (7.63.0-r0) (5/5) Installing curl (7.63.0-r0) Executing busybox-1.29.3-r10.trigger Executing ca-certificates-20190108-r0.trigger OK: 22 MiB in 28 packages Removing intermediate container 19fb009c38d1 ---> 6d1be09ddac1 Successfully built 6d1be09ddac1 Successfully tagged linuxea:26-1得到的大小是18.7M[root@linuxea.com ~]# docker images linuxea:26-1 REPOSITORY TAG IMAGE ID CREATED SIZE linuxea 26-1 6d1be09ddac1 9 seconds ago 18.7MB而层也随机变少了,只有5层,18.7M[root@linuxea.com ~]# docker history linuxea:26-1 IMAGE CREATED CREATED BY SIZE COMMENT 6d1be09ddac1 23 seconds ago /bin/sh -c apk add nginx && apk add php &… 13.1MB a9b2f826f6c9 9 days ago /bin/sh -c #(nop) LABEL maintainer=www.linu… 0B 5ee27f5e579a 9 days ago /bin/sh -c #(nop) MAINTAINER www.linuxea.com 0B caf27325b298 3 weeks ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B <missing> 3 weeks ago /bin/sh -c #(nop) ADD file:2a1fc9351afe35698… 5.53MB 思考?现在我们经过对比,多条RUN似乎会导致多层,并且比一条RUN,也就是同一个层的镜像构建的要小一些。那现在肯定有人会迷惑,这说明了什么?现在,我们不妨在看看,我们换个方式。[root@linuxea.com ~]# cat Dockerfile FROM alpine:3.9 as default MAINTAINER www.linuxea.com LABEL maintainer="www.linuxea.com" RUN apk add nginx RUN apk add php RUN apk add curl RUN apk del nginx php curl如上,假设,我此刻在Dockerfile中已经使用了在这三个软件,在构建的最后步骤,我希望删掉->依赖安装最小化原则。而后我在进行build[root@linuxea.com ~]# docker build -t linuxea:26-2 . Sending build context to Docker daemon 427kB Step 1/7 : FROM alpine:3.9 as default ---> caf27325b298 Step 2/7 : AINTAINER www.linuxea.com ---> Using cache ---> 5ee27f5e579a Step 3/7 : LABEL maintainer="www.linuxea.com" ---> Using cache ---> a9b2f826f6c9 Step 4/7 : RUN apk add nginx ---> Using cache ---> 116c307055ed Step 5/7 : RUN apk add php ---> Using cache ---> ec1cbef7f89e Step 6/7 : RUN apk add curl ---> Using cache ---> f4000a9883f1 Step 7/7 : RUN apk del nginx php curl ---> Running in 124fea0865fb (1/14) Purging nginx (1.14.2-r0) (2/14) Purging php7 (7.2.14-r0) (3/14) Purging php7-common (7.2.14-r0) (4/14) Purging curl (7.63.0-r0) (5/14) Purging pcre (8.42-r1) (6/14) Purging libedit (20181209.3.1-r0) (7/14) Purging ncurses-libs (6.1_p20190105-r0) (8/14) Purging ncurses-terminfo (6.1_p20190105-r0) (9/14) Purging ncurses-terminfo-base (6.1_p20190105-r0) (10/14) Purging libxml2 (2.9.9-r1) (11/14) Purging libcurl (7.63.0-r0) (12/14) Purging ca-certificates (20190108-r0) Executing ca-certificates-20190108-r0.post-deinstall (13/14) Purging nghttp2-libs (1.35.1-r0) (14/14) Purging libssh2 (1.8.0-r4) Executing busybox-1.29.3-r10.trigger OK: 6 MiB in 14 packages Removing intermediate container 124fea0865fb ---> 3a74abf45025 Successfully built 3a74abf45025 Successfully tagged linuxea:26-2构建完成。镜像的大小仍然是18.9M。[root@linuxea.com ~]# docker images linuxea:26-2 REPOSITORY TAG IMAGE ID CREATED SIZE linuxea 26-2 3a74abf45025 16 seconds ago 18.9MB我们使用docker history命令查看镜像,可见我们的确执行了apk del nginx php curl命令[root@linuxea.com ~]# docker history linuxea:26-2 IMAGE CREATED CREATED BY SIZE COMMENT 3a74abf45025 28 seconds ago /bin/sh -c apk del nginx php curl 21.2kB f4000a9883f1 6 minutes ago /bin/sh -c apk add curl 1.68MB ec1cbef7f89e 6 minutes ago /bin/sh -c apk add php 8.89MB 116c307055ed 6 minutes ago /bin/sh -c apk add nginx 2.74MB a9b2f826f6c9 9 days ago /bin/sh -c #(nop) LABEL maintainer=www.linu… 0B 5ee27f5e579a 9 days ago /bin/sh -c #(nop) MAINTAINER www.linuxea.com 0B caf27325b298 3 weeks ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B <missing> 3 weeks ago /bin/sh -c #(nop) ADD file:2a1fc9351afe35698… 5.53MB 我们进入容器确认是否被卸载[root@linuxea.com ~]# docker run --rm -it linuxea:26-2 sh / # curl sh: curl: not found / # 发生了什么?为什么在Dockerfile删除的软件已经消失,而层的大小并没有因为删除而缩减?要回答这一点,我们要返回上面的docker history linuxea:26-2中查看。我们将SIZE用加法运算,得到的大小与docker image查看的几乎接近,如下[root@linuxea.com ~]# docker history linuxea:26-2|awk '{print $NF}'|grep -v "COMMENT" 21.2kB 1.68MB 8.89MB 2.74MB 0B 0B 0B 5.53MB这说明docker会将所有的层相加。尽管我们在删除了这三个包,但是在上一层中,这三个包仍然是存在的,因为从下到上叠加的层最终构成了镜像。所以,要从层中有效删除,就需要在一个层中有效的组织。这些层是否就没有了作用?当在测试环境中,编写Dockerfile的时候可以使用多层,也就是多条RUN的命令来缓存每一个层做测试或者调试,当相同的指令就会复用缓存中间层。这可以有效的缩减build时间,原因是利用了docker的构建缓存。而构建缓存在CI/CD中是很有用的。RUN正确的使用方式我们应该将命令使用RUN和&&链接起来,这样在中间的层就表现为一个层,这样的情况并不利于调试,如上所述。每次构建都会认为是一条指令,当发生一条命令的变化,就无法使用缓存加速构建。我们重新build,并且删除缓存FROM alpine:3.9 as default MAINTAINER www.linuxea.com LABEL maintainer="www.linuxea.com" RUN apk add nginx \ && apk add php \ && apk add curl \ && apk del nginx php curl \ && rm /var/cache/apk/*重新build后的大小仅为5.56M[root@linuxea.com ~]# docker images linuxea:26-3 REPOSITORY TAG IMAGE ID CREATED SIZE linuxea 26-3 f300b5797d81 33 seconds ago 5.56MB[root@linuxea.com ~]# docker history linuxea:26-3 IMAGE CREATED CREATED BY SIZE COMMENT f300b5797d81 2 seconds ago /bin/sh -c apk add nginx && apk add php &&… 26kB a9b2f826f6c9 10 days ago /bin/sh -c #(nop) LABEL maintainer=www.linu… 0B 5ee27f5e579a 10 days ago /bin/sh -c #(nop) MAINTAINER www.linuxea.com 0B caf27325b298 3 weeks ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B <missing> 3 weeks ago /bin/sh -c #(nop) ADD file:2a1fc9351afe35698… 5.53MB 其他的办法我们在使用RUN命令的时候,除了尽可能的减少层,我们还可以使用docker多阶段构建Multi-Stage。docker多阶段构建将"上一次的构建用在下一个Dockerfile中",这很实用FROM golang:1.7.3 as builder WORKDIR /go/src/github.com/alexellis/href-counter/ RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . FROM alpine:latest WORKDIR /root/ COPY --from=builder /go/src/github.com/alexellis/href-counter/app .延伸阅读linuxea:如何利用docker构建缓存快速迭代?linuxea:docker多阶段构建Multi-Stage与Builder对比总结学习更多学习如何使用Docker CLI命令,Dockerfile命令,使用这些命令可以帮助你更有效地使用Docker应用程序。查看Docker文档和我的其他帖子以了解更多信息。docker目录白话容器docker-compose
2019年02月26日
5,179 阅读
0 评论
0 点赞
2019-02-25
linuxea:docker卷和文件系统权限
Docker容器是无状态的(不需要在运行时候做持久化数据)。一般来讲,一些有状态的,存储重要数据的应用需要一些持久存储。卷功能提供了一种支持此要求的方法,但它带有一些关于文件系统权限的问题。在大多数部署的设置中,将使用容器编排机制,并且持久存储由某些公共云产品提供,这些产品可能具有自己的配置权限的方式。但是,在本地开发或产品的早期迭代期间,最简单的方法是将主机目录公开为docker卷。简而言之,这些是将主机目录配置为卷时需要注意的事实:从主机和容器的角度来看,对卷中内容设置的文件权限是相同的。只有UID(用户ID)和GID(组ID)很重要。例如,用户和组的名称和密码不需要匹配,甚至不需要存在于主机和容器中根据自己的配置对在运行的容器,具有所有操作权限,如:强制执行文件权限。例如,如果用户A同时存在于主机和容器中,将用户A添加到主机上的组B,不允许用户A写入容器内的组B所拥有的目录,除非在容器内创建组B并且向其添加用户A.默认情况下,容器的命令以root身份运行(在基于unix的系统上)可以将文件/目录所有权设置为不属于任何实际组的GID如果你掌握上述事实,应该能够配置容器和卷而不会有太多意外事故。如果你不熟悉UNIX文件权限,我可以推荐本站的linuxea基础之权限管理一篇。权限我们可以方便地为本地开发配置内容,示例:将要用作卷的目录的组所有权设置为某些GID(在此示例中为1101)未在主机上的任何实际组上使用[root@linuxea.com ~]# mkdir /root/linuxea.com [root@linuxea.com ~]# chown :1101 /root/linuxea.com更改目录的权限以授予组成员完全访问权限(读取+写入+执行)[root@linuxea.com ~]# chmod 755 /root/linuxea.com确保文件夹中的所有之后内容都将继承组所有权[root@linuxea.com ~]# chmod g+s /root/linuxea.com/在Dockerfile中创建一个用户,该用户是1101组的成员看起来可以是这样FROM alpine:3.9 MAINTAINER www.linuxea.com LABEL maintainer="www.linuxea.com" RUN addgroup --gid 1101 www \ && adduser -u 1101 -S -H -s /bin/bash -g www -G www www -D(可选)将主机用户添加到组中,以便你方便地使用主机中的目录centos:useradd linuxea上面的示例是一个最小化设置,可确保你不以root身份运行容器命令,并且可以使用主机上附加卷的内容,而无需使用主机root用户(你必须确保你使用的非root用户启动容器。该设置在构建时执行硬编码配置,使你无法在运行时调整GID。如果需要,你必须将GID作为环境变量传递,并包含可以使用它的通用脚本。示例如下:entrypoint.sh#!/bin/bash # www.linuxea.com USER_ID=${LOCAL_USER_ID:-1101} USER_NAME=${LOCAL_USER_NAME:-www} echo "Starting with UID : $USER_ID And user $USER_NAME" addgroup --gid $USER_ID $USER_NAME adduser -u $USER_ID -S -H -s /bin/bash -g $USER_NAME -G $USER_NAME $USER_NAME -D # useradd --shell /bin/bash -u $USER_ID -o -c "" -m user export HOME=/home/$USER_NAME exec /usr/local/bin/gosu $USER_NAME "$@"我们在这里做的是从环境变量中获取UID和将要用到的名称,如果它不存在则默认为1101,NAME不存在则www,并且使用adduser/useradd设置UID时实际使用熟悉的命令创建用户“www” 。entrypoint创建最后我们用这个用户gosu来执行我们的流程"$@"。记住来自Dockerfile的CMD或来自docker CLI的命令作为命令行参数传递给entrypoint.sh脚本。我们在看DockerfileFROM alpine:3.9 MAINTAINER www.linuxea.com LABEL maintainer="www.linuxea.com" COPY entrypoint.sh /bin/entrypoint.sh RUN apk update \ && apk add bash \ && wget https://github.com/tianon/gosu/releases/download/1.11/gosu-amd64 -O /usr/local/bin/gosu \ && chmod +x /bin/entrypoint.sh /usr/local/bin/gosu \ && rm /var/cache/apk/* ENTRYPOINT ["entrypoint.sh"] CMD ["sleep","30000"]build[root@linuxea.com ~]# docker build -t marksugar/alpine:3.9 .run可以传递LOCAL_USER_ID和LOCAL_USER_NAME改变脚本参数,从而改变gid和name[root@linuxea.com ~]# docker run -d marksugar/alpine:3.9 00f7afb58a9ee069f1bca6fdd716131e48538bc161911e62bf4249328f98270b[root@linuxea.com ~]# docker exec -it 00f7 ps aux PID USER TIME COMMAND 1 www 0:00 sleep 30000 14 root 0:00 ps aux[root@linuxea.com ~]# ps aux|grep sleep 1101 27414 0.3 0.0 1520 4 ? Ss 21:56 0:00 sleep 30000现在你就可以下载marksugar/alpine:3.9镜像来作为基础镜像了。延伸阅读linuxea:了解uid和gid如何在docker容器中工作linuxea:docker容器中程序不应该以root用户身份运行学习更多学习如何使用Docker CLI命令,Dockerfile命令,使用Bash命令可以帮助你更有效地使用Docker应用程序。查看Docker文档和我的其他帖子以了解更多信息。docker目录白话容器docker-compose
2019年02月25日
2,815 阅读
0 评论
0 点赞
2019-02-23
linuxea:docker容器中程序不应该以root用户身份运行
容器中的进程不应以root身份运行,或者假设它们是root用户。正确的方式是使用已知的UID和GID在Dockerfile中创建用户,并以此用户身份运行你的进程。通过限制对资源的访问,遵循此模式的映像更容易安全运行概观精心设计的系统遵循最小特权原则。这简单地说明应用程序应该只能访问它所需的资源以执行其所需的功能。这在设计安全系统时至关重要。无论是恶意还是由于某些错误,进程可能会在运行时产生意外后果。保护自己免受任何意外的最佳方法之一是仅授予进程运行所需的最小权限。大多数容器化进程是应用程序服务,因此不需要root访问权限。虽然Docker需要root运行,但容器本身却不需要。编写良好,安全且可重用的Docker镜像不应该以root身份运行,并且应该提供一种可预测且简单的方法来限制访问。为什么这很重要请记住,在容器中运行的进程与在Linux上运行的其他进程没有什么不同,除了它有一小段元数据声明它在容器中。容器不是信任边界,因此,容器中运行的任何东西都应该与主机本身上运行的任何东西一样对待。就像你不会(或不应该)在服务器上以root身份运行任何东西一样,你不应该在服务器上的容器中以root身份运行任何东西。在别处创建的运行二进制文件需要大量的信任条件,对于容器中的二进制文件也是如此。如果容器内的进程默认以root身份运行,则可以在启动容器时更改uid和gid。作为镜像的作者,你应该默认以不同的用户身份运行,并且更容易限制该用户的访问权限。通过在Dockerfile中创建用户,你不仅可以使其默认安全,而且更容易保持安全。示例将显示以root身份运行容器的风险。让我们在/root目录中创建一个test.file文件,防止root 以外的任何人查看它:[root@linuxea.com ~]# echo "www.linuxea.com" >> ./test.file [root@linuxea.com ~]# chmod 600 test.file [root@linuxea.com ~]# ls -l total 4 -rw------- 1 root root 16 Feb 23 13:24 test.file而后切换到linuxea用户下[root@linuxea.com ~]# su - linuxea Last login: Wed Feb 20 17:44:35 CST 2019 on pts/2[linuxea@linuxea.com ~]$ cat /root/test.file cat: /root/test.file: Permission denied [linuxea@linuxea.com ~]$ ls /root/test.file ls: cannot access /root/test.file: Permission denied [linuxea@linuxea.com ~]$ ls -l /root/test.file ls: cannot access /root/test.file: Permission denied现在我在root家目录创建了一个test.file的文件,并且追加了信息,权限是600,只有root可以看到内容。以普通个用户登陆是没有权限查看的。现在,我创建一个Dockerfile镜像FROM alpine:latest MAINTAINER www.linuxea.com mark ENTRYPOINT ["cat","/opt/test.file"][linuxea@linuxea.com ~]$ docker build -t linuxea:23 . Sending build context to Docker daemon 10.24kB Step 1/3 : FROM alpine:latest ---> caf27325b298 Step 2/3 : MAINTAINER www.linuxea.com mark ---> Using cache ---> c9603327aeb5 Step 3/3 : ENTRYPOINT ["cat","/opt/test.file"] ---> Running in d1d8fd1f5fd6 Removing intermediate container d1d8fd1f5fd6 ---> 1bd2e84b67f9 Successfully built 1bd2e84b67f9 Successfully tagged linuxea:23而后我运行这个镜像,并且将不能查看的文件挂在到容器中并运行[linuxea@linuxea.com ~]$ docker run --rm -v /root/test.file:/opt/test.file linuxea:23 www.linuxea.com即使我是linuxea,容器正在运行root,因此可以访问root此服务器上的所有内容。这不理想; 以这种方式运行容器意味着你从Docker Hub中提取的每个容器都可以完全访问服务器上的所有内容(具体取决于你运行它的方式)。建议这里的建议是创建一个uid Dockerfile中已知的用户,并以该用户身份运行应用程序进程。Dockerfile的开头应遵循以下模式:FROM <centos:-基本镜像> RUN groupadd -g 999 USER && \ useradd -r -u 999 -g USER USER USER USER ... <Dockerfile的其余部分> ...使用此模式,可以在具有所需权限最少的用户/组的上下文中轻松运行容器。例如,我将从上面将它添加到我的Dockerfile并重新运行该示例。我的Dockerfile现在看起来像这样:FROM alpine:latest MAINTAINER www.linuxea.com mark RUN addgroup www && adduser -u 1001 -S -H -s /sbin/nologin -g 'nginx' -G www www USER www ENTRYPOINT ["cat","/opt/test.file"][linuxea@linuxea.com ~]$ docker build -t linuxea:24 . Sending build context to Docker daemon 10.24kB Step 1/5 : FROM alpine:latest ---> caf27325b298 Step 2/5 : MAINTAINER www.linuxea.com mark ---> Using cache ---> c9603327aeb5 Step 3/5 : RUN addgroup www && adduser -u 1001 -S -H -s /sbin/nologin -g 'nginx' -G www www ---> Running in 09d9fc94586a Removing intermediate container 09d9fc94586a ---> 85d82a2b1c7f Step 4/5 : USER www ---> Running in 9dd67807ba0e Removing intermediate container 9dd67807ba0e ---> c56e5dc43a7f Step 5/5 : ENTRYPOINT ["cat","/opt/test.file"] ---> Running in 31007cdf4293 Removing intermediate container 31007cdf4293 ---> e6dfe1c64d2c Successfully built e6dfe1c64d2c Successfully tagged linuxea:24使用与以前相同的命令运行此容器:[linuxea@linuxea.com ~]$ docker run --rm -v /root/test.file:/opt/test.file linuxea:24 cat: can't open '/opt/test.file': Permission denied重复使用其他图像Docker的镜像是很好用的,因为它们是可重用的。但是当FROM的镜像以非root身份运行时,你的容器将继承该非root用户。如果你需要创建自己的或以root身份执行操作,请确保USER root在Dockerfile顶部附近的某处。然后FROM User再次使其可用。FROM alpine:latest MAINTAINER www.linuxea.com mark USER root RUN make install 《root权限需要》 RUN addgroup www && adduser -u 1001 -S -H -s /sbin/nologin -g 'nginx' -G www www USER www ENTRYPOINT ["cat","/opt/test.file"]以非root用户身份运行其他容器Docker镜像设计为可移植的,从Docker Hub中提取其他镜像是正常的。其中一些(官方镜像)将遵循此最佳实践并作为普通用户帐户运行。但许多镜像都没有这样做。许多镜像只是运行root,并由你决定如何安全地运行它们。有几个选项可以让你安全地运行没有创建自己用户的镜像。创建另一个图像首先,一个选项是使用原始镜像作为FROM图层创建另一个镜像。然后,您可以创建用户帐户,并将原始ENTRYPOINT和CMD指令复制到自己的镜像。此结果镜像现在遵循此处概述的最佳实践,并且默认情况下将安全运行。这里的权衡是你需要在更新基本镜像时重建构建镜像。你必须在重建基本镜像时设置重建过程。你可以在原有的镜像之上修改,也可以从docker镜像复现Dockerfile进行重新编写。启动容器时指定uid最后,你可以在主机上创建用户,并uid在启动容器时将其传递给Docker。例如,重新访问原始示例Dockerfile:FROM alpine:latest MAINTAINER www.linuxea.com mark ENTRYPOINT ["cat","/opt/test.file"]我可以使用或不使用用户id参数运行此容器,并查看不同的结果(用户ID 1001是我自己在此服务器上的linuxea帐户):[linuxea@linuxea.com ~]$ docker run --rm --user=1001 -v /root/test.file:/opt/test.file linuxea:23 cat: can't open '/opt/test.file': Permission denied[linuxea@linuxea.com ~]$ docker run --rm -v /root/test.file:/opt/test.file linuxea:23 www.linuxea.com这与在Dockerfile中创建用户的工作方式相同,但它要求用户可选择安全地运行容器。在Dockerfile中指定非root用户将使容器默认安全运行。进一步了解linuxea:了解uid和gid如何在docker容器中工作linuxea:docker run的十个常用选项linuxea:十个初学Dcoker cli指令学习更多学习如何使用Docker CLI命令,Dockerfile命令,使用这些命令可以帮助你更有效地使用Docker应用程序。查看Docker文档和我的其他帖子以了解更多信息。docker目录白话容器docker-compose
2019年02月23日
9,241 阅读
0 评论
0 点赞
2019-02-22
linuxea:docker-compose设置静态ip和link与depends_on的区别
在容器中设置静态ip,这似乎又是一个过时的老话题,但是在讨论群中仍然有朋友为此感到困惑。我致力于解决这些小问题和在使用中容器落地的问题。为此,我又写了这篇文章来描述容器中使用静态ip,和不使用静态ip link的技巧。在正式配置docker-compose之前,我们需要先了解link,因为在我看来在容器中使用固定ip是件没有必要的事情,使用ip只是我们脑中长久的一个使用习惯。而在docker中link已经帮我们解决了这个麻烦事,并提供了更简单的方式。那么,通常来讲,在这个问题上产生疑问的,必然是在使用两个以上的容器。那就有必要了解depends_on。在使用link的同时,我当然也会叙述另外一个常用的选项depends_on,它非常有用。并且我会做简单的比较。阅读本篇文章,你将了解,docker-compose中3版本的使用,以及link使用方式和depends_on的技巧。容器间互联尽管,我们要解决的是单机网络,也不妨先简单介绍下跨主机和不跨主机容器间互联的区别不跨主机互相访问不跨主机互联可以采用默认的bridge网络,docker0桥在物理机上,而后创建的容器,容器内有eth0,另外一侧在物理机的docker0,而docker0可以理解成一个虚拟交换机。这样同一个交换机内的容器就可以直接进行互联。除此之外,还可以使用host网络模式,共用宿主机网络,这样一来容器和宿主机使用同一个网络,不隔离网络名称空间,网卡信息,就不存在网络上的问题。最后还可以采用联盟式容器解决网络问题跨主机容器互联容器跨主机访问实际上是做了DNAT,将容器发布出去,这些规则在iptables中可以看到Chain DOCKER (3 references) pkts bytes target prot opt in out source destination 0 0 ACCEPT tcp -- !br-77c0aabda308 br-77c0aabda308 0.0.0.0/0 172.18.0.2 tcp dpt:26379 0 0 ACCEPT tcp -- !br-77c0aabda308 br-77c0aabda308 0.0.0.0/0 172.18.0.2 tcp dpt:6379 0 0 ACCEPT tcp -- !br-77c0aabda308 br-77c0aabda308 0.0.0.0/0 172.18.0.3 tcp dpt:1194 0 0 ACCEPT tcp -- !br-77c0aabda308 br-77c0aabda308 0.0.0.0/0 172.18.0.3 tcp dpt:443而对于互相双方来讲,是看不到后面的容器的,访问的是通过端口转发到真正的容器端口上。如果你要跨主机访问,就不能使用容器的ip,只能使用宿主机的ip和容器映射的端口通过iptables转发访问。[root@linuxea.com ~]# telnet 172.25.50.250 6379 Trying 172.25.50.250... Connected to 172.25.50.250. Escape character is '^]'.当然,也有例外,如果是叠加的方式就不需要在物理机做端口映射,直接通过隧道访问对端ip和端口link连接我们了解到在容器网络中的分配ip是不固定的,倘若我在第一次使用的ip地址在后面使用中发生了改变,那可能不无法正常使用了!这显然并不是我们想要的。link就解决了这个问题。links似乎将会被弃用,因为它并不重要(不使用link仍然可以通过容器名称访问),我们主要来看这里的别名操作docker-compose如下:version: '3' services: redis: image: marksugar/redis:5.0.0 container_name: redis restart: always privileged: true environment: - REDIS_CONF=on - REQUIRE_PASS=OTdmOWI4ZTM4NTY1M2M4OTZh - MASTER_AUTH=OTdmOWI4ZTM4NTY1M2M4OTZh - MAXCLIENTS_NUM=600 - MAXMEMORY_SIZE=4096M volumes: - /etc/localtime:/etc/localtime:ro - /data/redis-data:/data/redis:Z - /data/logs:/data/logs ports: - '6379:6379' - '26379:26379' softether: image: marksugar/softether:4.27 links: - "redis:redisdb" container_name: softether4.27 restart: always ports: - '443:443' - '1194:1194'请注意,其中 links: - "redis:redisdb"softether和redis的容器ip地址分别是172.18.0.2和172.25.8.0.3,假设此时我们并不知道ip这里在softether中链接了redis,并设置了别名,redisdb。那也就是说我们可以使用redisdb来访问redis本身。[root@linuxea.com /opt/2019/net]# docker exec -it softether4.27 sh / # apk update fetch https://dl-cdn.alpinelinux.org/alpine/v3.8/main/x86_64/APKINDEX.tar.gz fetch https://dl-cdn.alpinelinux.org/alpine/v3.8/community/x86_64/APKINDEX.tar.gz v3.8.2-56-g4d33ed061d [https://dl-cdn.alpinelinux.org/alpine/v3.8/main] v3.8.2-53-g53558ad6fc [https://dl-cdn.alpinelinux.org/alpine/v3.8/community] OK: 9559 distinct packages available / # apk add redis (1/1) Installing redis (4.0.11-r0) Executing redis-4.0.11-r0.pre-install Executing redis-4.0.11-r0.post-install Executing busybox-1.28.4-r0.trigger OK: 68 MiB in 35 packages / # redis-cli -a OTdmOWI4ZTM4NTY1M2M4OTZh -h redisdb info Warning: Using a password with '-a' option on the command line interface may not be safe. # Server redis_version:5.0.0 redis_git_sha1:00000000 redis_git_dirty:0 redis_build_id:a7a8d032c5a69a3f redis_mode:standalone os:Linux 4.18.12-1.el7.elrepo.x86_64 x86_64 arch_bits:64 multiplexing_api:epoll atomicvar_api:atomic-builtin gcc_version:6.4.0 process_id:10 run_id:c6162ba2b02d70c1defda6073f863af1ccb207a6 tcp_port:6379 uptime_in_seconds:263 uptime_in_days:0 hz:10 configured_hz:10 lru_clock:7302247这说明了什么?我们完全可以使用容器的名称来进行访问,并不需要使用ip地址来指定。因为ip会变,名称却不会变,这是因为容器的hosts/ # cat /etc/hosts 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters 172.18.0.3 d374cbffc3b0在hosts中,已经写了ip和容器id的对应关系。你完全可以使用容器名称来进行访问。tips你可以不使用别名,直接使用容器名称进行访问,你会看到redis容器的ip地址/ # ping redis PING redis (172.18.0.2): 56 data bytes 64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.093 ms 64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.143 ms 64 bytes from 172.18.0.2: seq=2 ttl=64 time=0.106 ms 64 bytes from 172.18.0.2: seq=3 ttl=64 time=0.152 ms因为在同一个网络内。depends_on我们在看另外一个场景,假如此刻softether依赖于redis,在启动的时候就需要读取redis或者写入,通常情况下,redis必然要先启动,redis启动,softether在启动。这才是正确的方式,如果softether先启动,而redis还没有就绪,程序必然会报错,甚至崩溃。此时depends_on就有了用武之地如上场景中那般,配置如下:redis: image: marksugar/redis:5.0.0 ... softether: image: marksugar/softether:4.27 ... depends_on: - redis这样,softether会在redis启动之后启动。如果你有多个依赖,就可以按照顺序往后写,比如mysqlredis: image: marksugar/redis:5.0.0 ... mysql: image: ... softether: image: marksugar/softether:4.27 ... depends_on: - redis - mysql这样的顺序就是,softether会等待,先启动redis,在启动mysql,依次启动才到softether。由此可见,depends_on和links完全是两个不同的东西。tips:我非常有必要提醒,启动和准备就绪是两个概念 ,启动并不意味着一定就启动完成,就像点击开机并不意味着马上就进入桌面。其中的就绪状态则是另外的问题。请参阅启动顺序策略。docker-compose 静态ip默认情况下,docker会为容器分配随机(某种......)IP地址。通过使用链接,您可以将条目添加到容器的hosts文件中,并使用其IP地址映射另一个容器的名称。这样您就不需要知道其IP地址,只需使用其名称即可通过网络访问它。这种通过容器的hosts文件继而使用容器名称进行访问,这似乎已经解决了一大半人的问题。但是,我们仍然可以使用静态ip。上面我提到,网络是会发生改变的,为了彻底解决这一点,我们将网关,子网都设置好。在上面的默认网络中使用的ip是172.18.0.0网段。现在,我们修改它version: '3.7' services: redis: networks: linuxea: ipv4_address: 172.2.0.10 ... softether: networks: linuxea: ipv4_address: 172.2.0.11 ... networks: linuxea: ipam: driver: default config: - subnet: 172.2.0.0/24docker-compose如下[root@linuxea.com /opt/2019/net]# cat docker-compose.yaml version: '3.7' services: redis: image: marksugar/redis:5.0.0 container_name: redis restart: always privileged: true environment: - REDIS_CONF=on - REQUIRE_PASS=OTdmOWI4ZTM4NTY1M2M4OTZh - MASTER_AUTH=OTdmOWI4ZTM4NTY1M2M4OTZh - MAXCLIENTS_NUM=600 - MAXMEMORY_SIZE=4096M volumes: - /etc/localtime:/etc/localtime:ro - /data/redis-data:/data/redis:Z - /data/logs:/data/logs ports: - '6379:6379' - '26379:26379' networks: linuxea: ipv4_address: 172.2.0.10 softether: image: marksugar/softether:4.27 links: - "redis:redisdb" container_name: softether4.27 restart: always ports: - '443:443' - '1194:1194' networks: linuxea: ipv4_address: 172.2.0.11 networks: linuxea: ipam: driver: default config: - subnet: 172.2.0.0/24现在,你就可以使用静态的ip进行访问[root@linuxea.com /opt/2019/net]# docker-compose -f ./docker-compose.yaml up -d Creating redis ... done Creating softether4.27 ... done[root@linuxea.com /opt/2019/net]# docker exec -it softether4.27 sh / # ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 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 293: eth0@if294: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP link/ether 02:42:ac:02:00:0b brd ff:ff:ff:ff:ff:ff inet 172.2.0.11/24 brd 172.2.0.255 scope global eth0 valid_lft forever preferred_lft forever安装redis-client/ # apk update fetch https://dl-cdn.alpinelinux.org/alpine/v3.8/main/x86_64/APKINDEX.tar.gz fetch https://dl-cdn.alpinelinux.org/alpine/v3.8/community/x86_64/APKINDEX.tar.gz v3.8.2-56-g4d33ed061d [https://dl-cdn.alpinelinux.org/alpine/v3.8/main] v3.8.2-53-g53558ad6fc [https://dl-cdn.alpinelinux.org/alpine/v3.8/community] OK: 9559 distinct packages available / # apk add redis (1/1) Installing redis (4.0.11-r0) Executing redis-4.0.11-r0.pre-install Executing redis-4.0.11-r0.post-install Executing busybox-1.28.4-r0.trigger OK: 68 MiB in 35 packages通过静态ip链接/ # redis-cli -a OTdmOWI4ZTM4NTY1M2M4OTZh -h 172.2.0.10 Warning: Using a password with '-a' option on the command line interface may not be safe. 172.2.0.10:6379> info # Server redis_version:5.0.0 redis_git_sha1:00000000 redis_git_dirty:0 redis_build_id:a7a8d032c5a69a3f redis_mode:standalone os:Linux 4.18.12-1.el7.elrepo.x86_64 x86_64 arch_bits:64 multiplexing_api:epoll atomicvar_api:atomic-builtin gcc_version:6.4.0 process_id:10 run_id:cbf1e8eacc74da75c3dfcf797d104a8a2f95076e使用ipam可以为新网络定义特定的CIDR块,然后将每个容器连接到该网络,可以在该范围上指定其IP地址。现在,redis始终使用IP地址172.2.0.10运行,softether运行172.2.0.11,并且我能够在配置文件中对这些地址进行硬编码。延伸阅读linuxea:白话容器之虚拟化网络与容器网络(8)linuxea:白话容器之docker网络(9)学习更多学习如何使用Docker CLI命令,Dockerfile命令,使用这些命令可以帮助你更有效地使用Docker应用程序。查看Docker文档和我的其他帖子以了解更多信息。docker目录白话容器docker-compose
2019年02月22日
10,852 阅读
0 评论
1 点赞
1
2
3
...
13