Docker+K8s基礎篇(一)
- docker的介紹
- A:為什么是docker
- B:k8s介紹
- docker的使用
- A:docker的安裝
- B:docker的常用命令
- C:docker容器的啟動和操作
- docker鏡像的基礎管理
- A:docker鏡像的基礎概念
- B:docker鏡像的生成途徑
- C:鏡像的導入和導出
- 容器的虛擬化網絡
- A:容器虛擬化網絡基礎
- B:docker的網絡形式
- C:docker網絡的相關操作
- docker的存儲卷
- A:docker的存儲卷介紹
- B:docker的網絡形式
- C:docker網絡的相關操作
- dockerfile詳解
- A:dockerfile基礎
- B:dockerfile的語法格式
- C:dockerfile的指令詳解
- docker的私有registy
- A:docker的私有registy介紹
- B:docker的私有registy安裝和簡單使用
- C:Harbor安裝和簡單使用
-
portainer的安裝和使用
- A:portainer的安裝
♣一:docker的介紹
A:為什么是docker
隨着互聯網的用戶基數變大,可用於快速調配的系統資源環境變得尤為重要,在早些年,互聯網公司都基於虛擬化技術來完成生產及開發環境的快速搭建,隨着互聯網的發展越來越快,虛擬化的技術已經不能很好滿足需求。
主機級虛擬化的兩種形類型:
1:直接在硬件上安裝虛擬化軟件,再在上面安裝操作系統。
2:在宿主機之上安裝虛擬化軟件進行虛擬化,虛擬機和宿主機操作系統可以完全不一樣。例如:VMware
這些傳統的虛擬化技術最根本的本質就是要安裝一個操作系統的內核,內核提供了用戶空間,再在用戶空間里面跑進程,這些進程就需要單獨去安裝,例如tomcat等,底層的內核只是提供的資源的分派和調度。
這種形態下的虛擬化環境雖然能滿足我們的要求,但是不夠便捷,如果是想單純跑一個web服務,我們需要安裝操作系統,安裝web服務,然后配置才能提供服務,如果是新環境,你還得考慮版本兼容性等問題,是不是要裝新版的web服務等。而且是類型2虛擬化的,在還沒有提供web服務的之前,已經完成了兩級的調度和資源分派,第一層的宿主機和第二層的虛擬機,在這其中的資源浪費也是不言而喻的。
既然基礎層面上的繁瑣和資源消耗,那么從原理上來說最方便的就是去掉一層內核,但是去掉內核就會用戶空間就成了問題,因為用戶空間是為了跑真正提供服務的進程所存在的,例如我要100台nginx服務器都監聽8080端口,那么就需要100台虛擬機,一台虛擬機上是不能同時監聽兩個nginx的8080端口的,100台虛擬機的主要目的就是環境隔離,環境隔離了,即使其中50台nginx機器出了問題,那么我刪掉虛擬機就好了,不會影響我宿主機。
內核空間又內核提供,服務進程由用戶空間提供,n台虛擬機所需要實現的其實是用戶空間的隔離,我們需要解決的第一個難點就是在一個內核空間之上開盤N個相互隔離的用戶空間,讓服務進程之間不會相互影響。這些相互隔離的用戶空間就像一個大盒子里面裝的小盒子一樣,這種技術就叫容器技術。
容器技術最早是出現在FreeBSD上,叫jail,其主要的目的就是把進程之間相互隔離,並且限制這些散列的容器所消耗的資源得到控制。
進程相互隔離是做到了,但是底層的資源是有限的,當出現某一個進程占用資源已經到達容器的邊界,是不是就因為內存溢出等問題而讓服務掛掉了,顯然是不合理的,此時就需要做到動態的分配資源才是最可取的。
資源完成動態分配之后,那內存資源本來就是整個內存上划分的內存塊,如果是進程之間能通過ipc相互通信且能相互讀取內存中的信息,也是不合理的。
通過上面我們得知docker要完成的莫非就是自動化隔離,
在3.8版本內核上才完成了主機名和域名(UTS)的隔離,IPC,mount(掛載樹)和PID(進程樹)程序需要運行需要進程樹和文件樹,用戶和組,網絡這6大塊隔離技術(nameespaces名稱空間)再加上chroot技術完成了容器的基本功能。
可以看到這些技術說起來簡單,但是實現起來卻很難,可以看到上圖中用戶和組的隔離功能一致到3.8的內核版本才完全支持。
容器技術的最核心的隔離功能完成之后,還需要實現一個機制,那就是要實現類似我們VMware可以指定資源的總量是多少,例如宿主機有16G內存,但是一個容器只能我只給分派4G內存,不能出現一個容器因把內存泄露吃干抹凈剩余內存導致其它的容器無法正常運行,因為內存是不可壓縮的,用完就沒有了,相反cpu是有壓縮機制的,服務申請cpu運算,如果cpu占用過高,此時服務的請求就得排隊。
所以我們需要實現彈性的資源調配,我分給容器4G,這4G就是最大的值,當出現一個容器內部一個比較消耗內存的服務,那么就把容器中一個不常用的服務給kill掉,把空間讓出來給這個服務使用,這個機制在linux已經實現,叫cgroups,他將多個系統資源划分成一個組,在將這個組里面的內容分配到每一個用戶空間里面的進程上去。
雖然容器技術去掉了很多主機級虛擬化的弊端,但是容器技術也會存在問題,因為我們畢竟在一個內核之上分出來的一個個容器,但是這種隔離技術必然會出現漏洞,當一旦有漏洞,服務就可能繞過這個邊界去剝奪其他容器的資源,從而產生問題,一致到現在也沒有完全好的解決辦法,為了防止這種情況出現,一般用戶會開啟selinux等安全功能來對這些容器進行加固。
上面我們說簡要的說了下容器技術需要完成的功能,但是使用容器技術的工具卻是極為不方便,你需要安裝不同的工具甚至還要編寫代碼來完成容器的使用,既然如此,這種技術就被人給開發出來了,就叫做LXC(linuX Container),LXC極大的簡化了容器的操作難度,
例如我們可以使用lxc-create來快速的創建一個容器,但是lxc-create是怎么來完成快速創建的了,lxc-create是快速創建了一個模板,並且給這個模板執行權限,這個模板就會去一個指定好的倉庫里面把鏡像拉下來並chroot進去,完成文件和服務的安裝,創建用戶等,這樣過程其實和我們手動創建一個虛擬機是類似的。
lxc雖然簡化了我們使用容器技術的操作,但是依舊有很高的門檻,例如我想快速創建不通性質的容器怎么辦,我想大批量的進行容器的遷移怎么辦,容器出現故障銷毀了,里面存的數據和文件怎么辦,lxc在面臨分發和大規模使用上依然沒有找到很好的突破口,所以后面就出現了docker,從這個性質上來docker只不過是lxc的增強版,彌補了lxc存在的功能缺陷,docker也不是什么容器技術,只不過是方便我們使用容器技術的前段工具罷了,容器技術本身就是又內核去完成的,docker只不過站在了lxc的肩膀上。但是docker逐漸壯大之后就拋棄了lxc,換上自家研發的libcontainer
既然我們想實現容器技術的大規模使用會很難,哪怕是簡單的復刻一個一模一樣的容器,docker就開始在這些方面找解決辦法,docker在起初就是吧lxc重新封裝作為容器管理技術的引擎,lxc在快速部署容器的時候使用的模板工具不夠完善,docker發現之后,就開始把模板的思路擴展,研究出來一種鏡像的工具,在鏡像里面我們按照一個程序需要部署的所有步驟事先編排好,再把這個編排好的文件制作成一個鏡像文件,最后把這些鏡像統一放到一個便於管理的倉庫中,當用戶執行lxc命令的時候,不是去調用模板,而是去連到鏡像倉庫去下載一個匹配你當前需要的鏡像文件,然后基於鏡像啟動容器
這樣的方式就會極大的簡化用戶的操作,例如我想運行一個nginx,直接docker run nginx,這一條命令直接出發連接,下載,配置,啟動等一系列操作,之前的lxc是創建了一個用戶空間,一個用戶空間可以運行很多程序,但是docker不一樣,它完成的是一個容器運行一個程序。docker的這種邏輯給開發帶來了極大的便利,現在的開發環境都是異構的,要滿足不同的平台,centos,suse,windows等,對於docker來說我可以快速搭建相應的平台和服務用於開發中的調試,不管是基於java,python還是基於tomcat還是nginx,只要能事先編排好打包成鏡像,直接docker run就可以了。但是對運維來說是極為不便利,之前我一個用戶空間跑多個服務,可以共享某一個文件或者腳本,但是現在每一個程序都是單獨跑在單獨的容器之中,意味這文件是不能共享的,這樣會造成磁盤空間的浪費,其次以前我如果查看所有服務是否正常,直接ps等命令就能看,但是現有的情況下會變得很麻煩,我得進入每一個容器中去查看,意味着每一個容器里面都需要安裝相應的工具,更甚者,之前我能通過linux系統命令來調試的工具,結果到容器里面根本就沒有調試工具。
docker還有另外的一個功能,隨着docker的不斷發展和功能完善,有些情況下就想實現底層我基於centos的一個固定版本,但是上層我想跑不通的服務,基於這種想法,docker開發了分層管理的功能,我單獨創建一個centos,在上面安裝nginx,tomcat等服務,然后分別打包成鏡像,以后我想底層公用一個操作系統centos,上面我跑不同的服務,這樣的形式叫做聯合掛載技術,這每一個服務都被當做一個只讀文件被運行,這樣的形式能帶來好處,也會帶來資源的消耗,而且最重要的就是這每個只讀文件所產生的數據怎么辦,如果是遷移容器的話,服務不是難點,難的是數據,所以你要使用容器技術,就要實現考慮到長期保存的數據必須采用共享存儲系統,這樣你想遷移或者銷毀服務,沒有問題,數據還在就行,再把鏡像下載下來,和數據池掛載上,就能運行。
docker后來也制作的容器運行時的標准runC,也制作了鏡像文件標准,叫ocf開放容器格式標准。后來在linux基金會也創立了一個oci的標准,圍繞容器格式和運行時知道一個開放的工業化標准。
B:K8S介紹
docker能幫我們便捷的管理容器了,但是我們的生產場景是復雜的,生產環境中我們啟動程序是存在關聯關系的,下游程序的啟動必須依賴上游程序的啟動,這樣的操作我們不能是將容器創建好之后再去操作,我們應該是事先按照一定的順序和邏輯將其編排好。
隨着docker的編排越發變的重要之后,市面上也出現了大量的容器編排工具。
docker自己開發的工具:machine+swarm+compose,docker將這三個工具組合使用形成自己的編排工具;
asf研發的數據中心操作系統有一個統一資源編排工具mesos,mesos不是用來編排容器的,是用來實現統一資源調度的,要想編排工具就需要加一個中間層marathon;
kubernetes:k8s,基於go語言開發,k8s支持很多容器技術,docker只是其中一種。
♣二:docker的使用
A:docker的安裝和啟動
docker是使用鏡像來完成容器內容的創建的,在互聯網上,dockerhub.com是全球最大的鏡像倉庫網站,他提供了大多數的鏡像文件,除了docker提供的鏡像倉庫網站,阿里,等都有鏡像倉庫網站,除了網路源,你還可以搭建自己的私有鏡像倉庫。
docker要想完美的運行,需要內核版本在3.10以上,64位的操作系統,centos6的版本也能支持,但是因為紅帽公司將支持docker的補丁也挪到了6的版本上,但是穩定性就沒有7的版本,至少從某些層面來說。
docker的安裝文件在cenos7上直接yum就可以安裝,包文件在extras里面,自帶鏡像里面的docker比較老,我們在網絡上找一些共用的鏡像或者yum文件下載到本地使用。
可以參考網站清華大寫鏡像網站信息:清華大學開源鏡像網站

[root@localhost yum.repos.d]# wget https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/docker-ce.repo 下載清華源repo文件到本地的環境中 [root@localhost yum.repos.d]# cat docker-ce.repo [docker-ce-stable] name=Docker CE Stable - $basearch baseurl=https://download.docker.com/linux/centos/7/$basearch/stable enabled=1 gpgcheck=1 gpgkey=https://download.docker.com/linux/centos/gpg 但是我們發現雖然是清華的源,但是地址還是指向的docker的官網,因為docker的網站訪問太慢,我們改到清華源的地址 https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/7/x86_64/stable/Packages/ 把這個地址從linux的位置復制出來替換下docker-ce.repo文件的地址里面去 :%s@https://download.docker.com/@https://mirrors.tuna.tsinghua.edu.cn/docker-ce/@ vim的批量替換 yum repolist 源標識 源名稱 狀態 base/7/x86_64 CentOS-7 - Base 10,019 docker-ce-stable/x86_64 Docker CE Stable - x86_64 36 extras/7/x86_64 CentOS-7 - Extras 371 updates/7/x86_64 CentOS-7 - Updates 1,163 repolist: 11,589 [root@localhost yum.repos.d]#可以看到Docker CE Stable - x86_64的包 安裝 [root@localhost yum.repos.d]# yum -y install docker-ce ce和ee是docker的社區版和企業版 docker安裝成功

[root@localhost docker]# cat daemon.json { "registry-mirrors":["https://registry.docker-cn.com"] } [root@localhost docker]# systemctl start docker.service [root@localhost docker]# ps -aux | grep docker root 18867 0.1 3.2 781688 61256 ? Ssl 20:34 0:06 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock root 20119 0.0 0.0 112724 988 pts/1 S+ 21:52 0:00 grep --color=auto docker [root@localhost ~]# docker version 可以通過version查看客戶端和服務器端的版本 Client: Version: 18.09.3 API version: 1.39 Go version: go1.10.8 Git commit: 774a1f4 Built: Thu Feb 28 06:33:21 2019 OS/Arch: linux/amd64 Experimental: false Server: Docker Engine - Community Engine: Version: 18.09.3 API version: 1.39 (minimum version 1.12) Go version: go1.10.8 Git commit: 774a1f4 Built: Thu Feb 28 06:02:24 2019 OS/Arch: linux/amd64 Experimental: false [root@localhost ~]# docker info 還可以通過info查看更加詳細的信息 Containers: 0 容器的個數 Running: 0 運行中容器的個數 Paused: 0 暫停的容器個數 Stopped: 0 以停止的容器的個數 Images: 0 Server Version: 18.09.3 Storage Driver: overlay2 存儲后端引擎,這個很重要,docker早期的版本上是不支持這個存儲格式的,那時候主流的centos版本還是6,docker還是使用的dm格式,也就是基於lvm(邏輯卷)的形式,這種格式性能很差,還不是很穩定,在后續的docker版本上就換成了這種格式,centos直到7上才完全兼容overlay2的格式,所以說想很好的使用docker還是需要用到centos7版本 Backing Filesystem: xfs 備份的文件系統 Supports d_type: true Native Overlay Diff: true Logging Driver: json-file Cgroup Driver: cgroupfs Plugins: 支持的插件 Volume: local Network: bridge host macvlan null overlay Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog Swarm: inactive Runtimes: runc Default Runtime: runc Init Binary: docker-init containerd version: e6b3f5632f50dbc4e9cb6288d911bf4f5e95b18e runc version: 6635b4f0c6af3810594d2770f662f34ddc15b40d init version: fec3683 Security Options: seccomp Profile: default Kernel Version: 3.10.0-957.el7.x86_64 Operating System: CentOS Linux 7 (Core) OSType: linux Architecture: x86_64 CPUs: 4 Total Memory: 1.777GiB Name: localhost.localdomain ID: BXLI:3OWV:CIBE:BYC2:77XQ:ZJ3Y:736U:Y7M5:FXEW:ONYQ:M6S7:OKWI Docker Root Dir: /var/lib/docker Debug Mode (client): false Debug Mode (server): false Registry: https://index.docker.io/v1/ Labels: Experimental: false Insecure Registries: 127.0.0.0/8 Registry Mirrors: https://registry.docker-cn.com/ 加速的地址 Live Restore Enabled: false Product License: Community Engine
Storage Driver: overlay2參考https://blog.csdn.net/vchy_zhao/article/details/70238690
B:docker的常用命令
docker search:根據指定的關鍵字搜索鏡像

[root@localhost ~]# docker search nginx NAME DESCRIPTION STARS OFFICIAL AUTOMATED nginx 沒有增加/斜杠的代表頂級鏡像,一般是docker官方提供的 Official build of Nginx. 11053 [OK] jwilder/nginx-proxy 這種加了斜杠的斜杠前面是注冊者的賬戶名,這種鏡像一般叫做用戶鏡像 Automated Nginx reverse proxy for docker con… 1556 [OK] richarvey/nginx-php-fpm Container running Nginx + PHP-FPM capable of… 688 [OK] jrcs/letsencrypt-nginx-proxy-companion LetsEncrypt container to use with nginx as p… 492 [OK] kitematic/hello-world-nginx A light-weight nginx container that demonstr… 123 webdevops/php-nginx Nginx with PHP-FPM 123 [OK] zabbix/zabbix-web-nginx-mysql Zabbix frontend based on Nginx web-server wi… 91 [OK] bitnami/nginx Bitnami nginx Docker Image 64 [OK] linuxserver/nginx An Nginx container, brought to you by LinuxS… 56 1and1internet/ubuntu-16-nginx-php-phpmyadmin-mysql-5 ubuntu-16-nginx-php-phpmyadmin-mysql-5 49 [OK] tobi312/rpi-nginx NGINX on Raspberry Pi / armhf 24 [OK] nginx/nginx-ingress NGINX Ingress Controller for Kubernetes 17 wodby/drupal-nginx Nginx for Drupal container image 12 [OK] nginxdemos/hello NGINX webserver that serves a simple page co… 12 [OK] blacklabelops/nginx Dockerized Nginx Reverse Proxy Server. 12 [OK] schmunk42/nginx-redirect A very simple container to redirect HTTP tra… 11 [OK] centos/nginx-18-centos7 Platform for running nginx 1.8 or building n… 10 centos/nginx-112-centos7 Platform for running nginx 1.12 or building … 7 nginxinc/nginx-unprivileged Unprivileged NGINX Dockerfiles 4 1science/nginx Nginx Docker images that include Consul Temp… 4 [OK] mailu/nginx Mailu nginx frontend 3 [OK] travix/nginx NGinx reverse proxy 2 [OK] toccoag/openshift-nginx Nginx reverse proxy for Nice running on same… 1 [OK] wodby/nginx Generic nginx 0 [OK] ansibleplaybookbundle/nginx-apb An APB to deploy NGINX 0 [OK] [root@localhost ~]#
docker pull:將鏡像拉到本地
在dockerhub官網上每一個鏡像都有一個alpine的版本,可以看到這個版本所占的空間非常小,如果是用於開發調試可以使用這個版本,因為體積小下載速度快,但是缺點就是沒有相應的操作工具,如果是下載功能全的版本,占用空間也大也消耗帶寬,所以最好的辦法就是自己制作私有的鏡像倉庫

[root@localhost ~]# docker image pull nginx:1.14-alpine pull的時候指定版本信息,不確定的話可以上網站查一下 1.14-alpine: Pulling from library/nginx 6c40cc604d8e: Pull complete 76679ad9f124: Pull complete 389a52582f93: Pull complete 496e2dd2b91a: Pull complete Digest: sha256:b96aeeb1687703c49096f4969358d44f8520b671da94848309a3ba5be5b4c632 Status: Downloaded newer image for nginx:1.14-alpine [root@localhost ~]# docker images 可以看到本地已經與鏡像了 REPOSITORY TAG IMAGE ID CREATED SIZE nginx 1.14-alpine 66952fd0a8ef 5 weeks ago 16MB [root@localhost ~]#

通過另外一種命令下載linux上的工具包。 [root@localhost ~]# docker pull busybox Using default tag: latest latest: Pulling from library/busybox 697743189b6d: Pull complete Digest: sha256:061ca9704a714ee3e8b80523ec720c64f6209ad3f97c0ff7cb9ec7d19f15149f Status: Downloaded newer image for busybox:latest [root@localhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE busybox latest d8233ab899d4 3 weeks ago 1.2MB nginx 1.14-alpine 66952fd0a8ef 5 weeks ago 16MB [root@localhost ~]# 早期的docker是沒有加image來執行的
docker images:列出本地所有鏡像

[root@localhost ~]# docker image Usage: docker image COMMAND Manage images Commands: build Build an image from a Dockerfile history Show the history of an image import Import the contents from a tarball to create a filesystem image inspect Display detailed information on one or more images load Load an image from a tar archive or STDIN ls List images 查看 prune Remove unused images pull Pull an image or a repository from a registry 拉鏡像到本地 push Push an image or a repository to a registry rm Remove one or more images save Save one or more images to a tar archive (streamed to STDOUT by default) tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE Run 'docker image COMMAND --help' for more information on a command. [root@localhost ~]#
image后面可以接的參數都是新版本的docker提供的,主要是提供了分層。
新的版本也保留了老版本的命令,docker --help能查看到老版本和新版本的命令
docker image rm(新)或者老docer rmi :刪除鏡像

[root@localhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE busybox latest d8233ab899d4 3 weeks ago 1.2MB nginx 1.14-alpine 66952fd0a8ef 5 weeks ago 16MB [root@localhost ~]# docker image rm busybox Untagged: busybox:latest Untagged: busybox@sha256:061ca9704a714ee3e8b80523ec720c64f6209ad3f97c0ff7cb9ec7d19f15149f Deleted: sha256:d8233ab899d419c58cf3634c0df54ff5d8acc28f8173f09c21df4a07229e1205 Deleted: sha256:adab5d09ba79ecf30d3a5af58394b23a447eda7ffffe16c500ddc5ccb4c0222f [root@localhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE nginx 1.14-alpine 66952fd0a8ef 5 weeks ago 16MB [root@localhost ~]#

[root@localhost ~]# docker image ls REPOSITORY TAG (標簽) IMAGE ID(容器id) CREATED (時間) SIZE(大小) nginx 1.14-alpine 66952fd0a8ef 5 weeks ago 16MB [root@localhost ~]# docker image ls --no-trunc REPOSITORY TAG IMAGE ID CREATED SIZE nginx 1.14-alpine sha256:66952fd0a8efa0598626fad89d3a0827bc24fc92c3adb576adbc9fd58606e1af 5 weeks ago 16MB [root@localhost ~]# 可以看到IMAGE ID(容器id)就以完整的形式展現出來了,默認只顯示前面一段的
docker container run 創建容器之后直接啟動
docker container start 啟動容器
docker container stop 停止容器
docker container kill 強行停止容器

[root@localhost ~]# docker container --help Usage: docker container COMMAND Manage containers Commands: attach Attach local standard input, output, and error streams to a running container commit Create a new image from a container's changes cp Copy files/folders between a container and the local filesystem create Create a new container diff Inspect changes to files or directories on a container's filesystem exec Run a command in a running container export Export a container's filesystem as a tar archive inspect Display detailed information on one or more containers kill Kill one or more running containers logs Fetch the logs of a container ls List containers pause Pause all processes within one or more containers port List port mappings or a specific mapping for the container prune Remove all stopped containers rename Rename a container restart Restart one or more containers rm Remove one or more containers run Run a command in a new container start Start one or more stopped containers stats Display a live stream of container(s) resource usage statistics stop Stop one or more running containers top Display the running processes of a container unpause Unpause all processes within one or more containers update Update configuration of one or more containers wait Block until one or more containers stop, then print their exit codes Run 'docker container COMMAND --help' for more information on a command. [root@localhost ~]#

[root@localhost ~]# docker container ls CONTAINER ID(容器啟動的id) IMAGE (基於那個鏡像啟動的容器) COMMAND (在容器中運行了什么命令) CREATED(創建容器的時間) STATUS (當前狀態) PORTS (映射的端口) NAMES(容器的名稱) [root@localhost ~]#
docker的網絡,dockr一旦啟動就會創建一個docker0的網絡,而且默認的 172.17.0.1網段,這個網絡默認也是nat的方式,這就意味這它可以完成地址轉換,轉換成能對外通訊的地址。默認也是走在bridge上的。
C:docker容器的啟動和相關操作

[root@localhost ~]# docker run --name(給容器取一個名字) docker01 -it(進入交互式鏡像) busybox:latest(latest是標簽,可以選擇不加) Unable to find image 'busybox:latest' locally latest: Pulling from library/busybox 697743189b6d: Pull complete Digest: sha256:061ca9704a714ee3e8b80523ec720c64f6209ad3f97c0ff7cb9ec7d19f15149f Status: Downloaded newer image for busybox:latest / # busybox默認是進入shell終端窗口的

在busybox容器里面啟動httpd服務 在busybox里面創建html的網頁目錄,編輯網頁信息 然后啟動httpd -f -h /var/www/html/服務 然后另外開一個窗口訪問 [root@localhost ~]# curl 172.17.0.2 hello busybox.server!! [root@localhost ~]# 可以看到就能訪問到信息 [root@localhost ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 27bb5e91ac1f busybox:latest "sh" 9 minutes ago Up 9 minutes docker001 [root@localhost ~]# 因為當前我們啟動的進程是屬於sh這個用戶空間里面的,創建的httpd服務屬於sh這個進程的子進程,他擁有了進程樹和文件樹,所有也就可以訪問了,現在這個httpd服務只能在本地進行訪問,因為沒有做地址映射。

docker run --name docker001 -it busybox:latest 我們現在使用的是run直接啟動,當我們exit退出之后,容器也就暫停了,默認用docker ps是不顯示的,需要加參數才能看見 [root@localhost ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 27bb5e91ac1f busybox:latest "sh" 20 minutes ago Exited (137) 2 minutes ago docker001 0a4f4aeee033 busybox:latest "sh" 3 hours ago Exited (0) 3 hours ago docker02 b4ddc6132f1b busybox:latest "sh" 4 hours ago Exited (1) 4 hours ago docker01 [root@localhost ~]# docker start -ai(這兩個參數可以不加) docker001 / # ls bin dev etc home proc root sys tmp usr var / # 我們可以利用啟動的命令江處於暫停的容器啟動起來 /var/www/html # [root@localhost ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 27bb5e91ac1f busybox:latest "sh" 26 minutes ago Up About a minute docker001 [root@localhost ~]# 可以看到容器又處於up狀態 [root@localhost ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 27bb5e91ac1f busybox:latest "sh" 30 minutes ago Up 27 seconds docker001 0a4f4aeee033 busybox:latest "sh" 4 hours ago Exited (0) 3 hours ago docker02 b4ddc6132f1b busybox:latest "sh" 4 hours ago Exited (1) 4 hours ago docker01 [root@localhost ~]# docker kill docker001 可以對處於運行狀態的容器進行強停,如果是沒有必要千萬不要強停容器,可能會造成數據丟失 docker001 [root@localhost ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 27bb5e91ac1f busybox:latest "sh" 30 minutes ago Exited (137) 2 seconds ago docker001 0a4f4aeee033 busybox:latest "sh" 4 hours ago Exited (0) 3 hours ago docker02 b4ddc6132f1b busybox:latest "sh" 4 hours ago Exited (1) 4 hours ago docker01 [root@localhost ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 27bb5e91ac1f busybox:latest "sh" 30 minutes ago Exited (137) 2 seconds ago docker001 0a4f4aeee033 busybox:latest "sh" 4 hours ago Exited (0) 3 hours ago docker02 b4ddc6132f1b busybox:latest "sh" 4 hours ago Exited (1) 4 hours ago docker01 [root@localhost ~]# docker rm docker01 使用rm后面接容器名稱刪除容器 docker01 [root@localhost ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 27bb5e91ac1f busybox:latest "sh" 32 minutes ago Exited (137) About a minute ago docker001 0a4f4aeee033 busybox:latest "sh" 4 hours ago Exited (0) 3 hours ago docker02 [root@localhost ~]#

[root@localhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE busybox latest d8233ab899d4 3 weeks ago 1.2MB nginx 1.14-alpine 66952fd0a8ef 5 weeks ago 16MB [root@localhost ~]# docker run --name nginx001 -d(放到后台運行,此時nginx不需要和busybox一樣需要交互放后台即可) nginx:1.14-alpine 04e64051bd39c390dfdc618d4f02ef863cef99210c29e353e5ffc33f3352cbf3 [root@localhost ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 04e64051bd39 nginx:1.14-alpine "nginx -g 'daemon of…(daemon of表示不要跑在后台)" 14 seconds ago Up 12 seconds 80/tcp nginx001 nginx -g 'daemon of…(daemon of表示不要在容器中運行在后台) 在docker容器中運行任何程序一定不能讓它跑在容器的后台,一旦跑在后台,docker就認為程序沒有運行,而且你及時手動啟動還是會立馬終止 [root@localhost ~]# docker inspect nginx001通過inspect 來查看服務運行在哪個地址上 。。。。。。 "Gateway": "172.17.0.1", "IPAddress": "172.17.0.2", 可以看到運行在172.17.0.2地址上 "IPPrefixLen": 16, "IPv6Gateway": "", [root@localhost ~]# curl 172.17.0.2 訪問該地址 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html> [root@localhost ~]#
我們是用run的時候,如果本地沒有鏡像,run回去加速鏡像網站去找與之匹配的鏡像,但是你要保證在鏡像加速網站能找到該鏡像。

我們還可以通過exec參數繞過容器的邊界到里面去執行一些命令 [root@localhost ~]# docker exec nginx001 -it /bin/sh OCI runtime exec failed: exec failed: container_linux.go:344: starting container process caused "exec: \"-it\": executable file not found in $PATH": unknown [root@localhost ~]# docker exec -it nginx001 /bin/sh / # ls bin dev etc home lib media mnt opt proc root run sbin srv sys tmp usr var / # ps PID USER TIME COMMAND 1 root 0:00 nginx: master process nginx -g daemon off; 6 nginx 0:00 nginx: worker process 25 root 0:00 /bin/sh 31 root 0:00 ps / # netstat -anptu Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1/nginx: master pro / # 可以看到端口也監聽了 / # nginx-debug 2019/03/12 13:32:16 [emerg] 34#34: bind() to 0.0.0.0:80 failed (98: Address in use) nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address in use) 2019/03/12 13:32:16 [emerg] 34#34: bind() to 0.0.0.0:80 failed (98: Address in use) nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address in use) 2019/03/12 13:32:16 [emerg] 34#34: bind() to 0.0.0.0:80 failed (98: Address in use) nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address in use) 2019/03/12 13:32:16 [emerg] 34#34: bind() to 0.0.0.0:80 failed (98: Address in use) nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address in use) 2019/03/12 13:32:16 [emerg] 34#34: bind() to 0.0.0.0:80 failed (98: Address in use) nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address in use) 2019/03/12 13:32:16 [emerg] 34#34: still could not bind() nginx: [emerg] still could not bind() / # 我們還可以執行nginx相關的命令
我們發現docker在部署啟動服務優勢很明顯,但是docker給我們弄了一個難題就是我們要改服務的配置文件。

[root@localhost ~]# docker logs nginx001 172.17.0.1 - - [12/Mar/2019:13:17:50 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-" 可以看到我們之前訪問nginx服務的信息 [root@localhost ~]# 因為我們容器里面就跑了一個進程,可以使用logs參數直接獲取服務日志信息,當然這種情況下查看日志比較適合調試的過程
在上圖里面容器的創建方式有兩種,一種是run,創建之后直接啟動,還是就是create,創建之后不啟動,需要手動啟動,容器一旦啟動起來就處於runing狀態,這個狀態下可以kill,stop操作,這個時候容器就處於stopped停止狀態,這個狀態下可以rm刪除,刪除就處於deleted狀態,這個狀態就沒有操作了。圖上還有一個paused暫停狀態。
如果服務超過限制的內存最大值,將會被oom掉,這個時候就處於掛機的狀態,如果你這個時候有重啟的策略,服務將被重啟,沒有的話就會回到停止狀態。
docker的簡單的使用流程就是在這幾個狀態之間來回轉換的,只要知道什么狀態下該需要做什么操作,docker的簡單使用就變得簡單了。
♣三:docker鏡像基礎管理
A:docker鏡像的基礎概念
在docker的基礎使用的時候就反復提到了鏡像,我們在使用docker host它是由運行在docker里面的守護進程組成的,也就是docker的server端,接收客戶端的請求是由http或https與后台交互,而且docker的客戶端可以是遠程主機。
當docker的server端接收到來自client的請求的時候會在docker host里面創建或者啟動一個容器,在一個docker host里面可以創建和啟動各種不同的容器,而容器的啟動或運行是基於鏡像來實現的,當鏡像本地沒有,docker會自動連到網絡例如dockerhub上去獲取鏡像,獲取的鏡像會存儲到本地一個專門用於存儲鏡像的空間里面,這個存儲空間要求得是特殊的文件系統,這就是我們之前docker info看到的Storage Driver: overlay2,這些鏡像放在這個存儲空間里面是只讀的,而你下載的鏡像(應用程序)也是被下載倉庫名,而一個倉庫里面可以容納多個相同服務不同版本的鏡像,這些不同的版本會通過標簽來區分。
docker鏡像是含有啟動容器所需要的文件系統及其內容,因此鏡像的主要功能是創建並啟動docker容器。
docker另外一個概念必須要知道的是分層構建機制,類似於建房子,docker的地基層為bootfs,在上層則是rootfs,bootfs和linux啟動的是需要加載boot分區一樣,主要用於系統引導的文件系統,也提供了內核,容器啟動完成之后就會被卸載(只是從內存中移除),便於回收資源,bootfs的加載也是為了引導rootfs而存在的,rootfs就是一個根文件系統,也就是一個用戶空間。
在傳統的centos6等版本在啟動時候,內核掛載rootfs的時候會議只讀模式掛載,以便於完成系統自檢,這種機制是為了保證系統在自檢的時候因出現錯誤導致誤刪除數據,當系統自檢完成確保沒有問題,再將其重新掛載成讀寫模式。而在docker中,這個rootfs始終都是只讀的形式掛載的,后面通過(聯合掛載)技術額外掛載一個可寫層。
在docker中rootfs一旦啟動成功便可以在這個基礎之上在去安裝其他的鏡像,而這個原始底層鏡像是不會變動的,例如我們安裝了一個最簡化的centos系統,而這個centos是沒有操作工具的,我們需要安裝vim等一系列操作工具,這個時候你再安裝的vim工具就是基於最底層centos鏡像之上的,這個層你可以理解為vim層,只包含vim工具,然后你再安裝tomcat,這個tomcat就是在vim層之上,當你啟動tomcat的時候,需要從下層往上一層層的掛載,直到tomcat層,這個就是聯合掛載,當你需要對層級里面的文件進行修改的時候,僅能通過整個掛載層最上端的writable(可寫)層來實現,當容器被刪除的時候,這個writable也會被刪除
docker的聯合掛載和分層構建必須依賴於專門的文件系統的支撐才能實現,而早期的時候docker是通過aufs(高級多層統一文件系統)來實現linux的聯合掛載技術的文件系統,而aufs的前身是unionfs文件系統,據說unionfs這個文件系統申請合並到內核的時候因代碼比較糟糕曾多次被李納斯駁回,后來有人將unionfs重構變成aufs,但是依舊很糟糕,從此aufs這種文件系統如果是想用的話就必須向內核打補丁,從此市場主流的系統就只有ubuntu很早的時候支持了aufs文件系統,因為ubuntu將aufs整合到了內核中,也就意味着早起想使用docker也沒有很好的系統選擇,后來出現了overlayfs(疊加多層級文件系統),這個文件系統在3.18的版本開始就被整合到linux內核之中。除此之外docker還支持btrfs,devicemapper和vfs等文件系統
而市場上主流的centos既不支持aufs,也不支持overlayfs,這個時候centos就開始主推他的devicemapper文件系統,devicemapper也被拿出去和其它文件系統做比較,結果也發現devicemapper不盡人意。
從此在新版本的docker上會發現都是基於overlayfs文件系統來實現的了,overlayfs是一種抽象的二級文件系統,他需要構建與一個本地文件系統之上,所有我們使用docker info的時候發現backing Filesystem:xfs(后端是基於xfs),前端是overlayfs文件系統。
我們在制作好鏡像之后,鏡像應該有一個統一存儲的位置,這個位置就叫做docker registry,當容器啟動的時候,docker daemon會試圖從本地獲取相關的鏡像,本地沒有的時候,才去registry(這個registry沒有特別指定的時候就是指的dockerhub)中下載鏡像本保存在本地。
所以docker的鏡像可以是從dockerhub上提供的,也可以是本地的私有鏡像倉庫,而在本地需要創建私有的倉庫的時候可以通過docker提供的軟件包老實現,但是你通過軟件包制作的鏡像必須支持https,因為docker daemon默認必須是https的形式才能被訪問,如果是需要使用本地的你得改配置docker daemon,告訴docker daemon雖然是http不夠安全,但是能用。
docker registry的分類
第三方的registry,提供給客戶和docker社區使用;
第三方的registry,提供給客戶使用(之前的加速器就是這種);
由服務商的提供的registry,比如你買了紅帽的操作系統;
有能力建設防火牆和安全層的私有實體registry,這種私有的registry是最符合公司內部需求的,因為共有的registry實在難以滿足所有客戶。
docker registry的組成:
docker registry一般由兩部分組成,一部分是由不同程序的不同版本組成的鏡像倉庫,還有一個就是索引,索引的功能可以方便用戶根據之前定義好的信息來找相關的鏡像。
docker的私有倉庫一般都由專門的鏡像開發的人員在鏡像網站pull下來一個鏡像,在這個鏡像基礎上進行二次調整之后放到本地的私有倉庫上,然后運維人員將這些鏡像pull到需要部署的機器上。
在互聯網上有很多很好的鏡像倉庫站點,例如https://quay.io/repository/coreos/flannel, 這些站點在下載鏡像的時候,需要指定倉庫的地址和標簽,不是和我們之前直接指定應用程序即可,例如我們在quay.io上下載一個鏡像的方法:docker pull quay.io/bitnami/tomcat:8.5-ol-7 這個pull就是網站的信息加上標簽。
B:docker鏡像的生成途徑:
docker鏡像生成的途徑一般分為以下幾種:
1:我們可以使用dockerfile,我們自己使用一個命令docker build來制作,這個是專門用來做鏡像的。
2:我們可以基於某個容器來制作,例如某個容器已經處於運行當中,我們使用docker commit在容器的最上可寫層單獨創建一個鏡像層。
3:還有就是基於dockerhub自動生成的,這種類型的鏡像嚴格來說不算一種方式,dockerhub上的也是基於dockerfile方式產生的,最多算的上制作的一一種途徑而已。
基於某個容器來制作鏡像:
我們首先啟動一個容器,在容器中做好你打算做好的修改,這個我們需要使用到docker commit命令來完成。

[root@localhost ~]# docker run --name box001 -it busybox 我們首先啟動一個容器 / # ls bin dev etc home proc root sys tmp usr var / # mkdir -p /data/html 然后創建一個存html網頁的目錄 / # vi /data/html/index.html 並且創建一個網頁的內容 / # 此時我們如果結束容器,這個網頁肯定就不存在了,我們現在就想把這個網頁給制作成一個鏡像,現在的busybox是不能關閉的,一結束容器就暫停了。 [root@localhost ~]# docker commit -h 先看下commit的用法和參數 Flag shorthand -h has been deprecated, please use --help Usage: docker commit [OPTIONS] CONTAINER(基於那個容器做鏡像) [REPOSITORY(屬於哪個倉庫)[:TAG(有什么標簽)]] 如果是把后面的[REPOSITORY[:TAG]]省略,該鏡像就是本地的一個裸鏡像,不屬於任何倉庫也沒有標簽。 Create a new image from a container's changes Options: -a, --author string Author (e.g., "John Hannibal Smith <hannibal@a-team.com>") -c, --change list Apply Dockerfile instruction to the created image -m, --message string Commit message -p, --pause Pause container during commit (default true) -p參數是用暫停下容器使用,因為我們在制作鏡像的時候容器是處於運行狀態,這個狀態會不斷產生很多的文件,我們讓其暫停下在去制作。 [root@localhost ~]# docker commit -p box001 sha256:25a0f192ba8ab5f56eb9a6e068f617799fb5e8d4fabe0a12f39176dcbf7c7cf9 [root@localhost ~]# 一個鏡像就制作完成,我們使用dokcer images查看下信息 [root@localhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE <none> <none> 25a0f192ba8a 53 seconds ago 1.2MB busybox latest d8233ab899d4 4 weeks ago 1.2MB nginx 1.14-alpine 66952fd0a8ef 6 weeks ago 16MB [root@localhost ~]#可以看到我們沒有指定倉庫名,也標簽信息,就是能看到image id和大小,如果鏡像的標簽還沒有規划好,這個標簽我們也是可以后補的,使用docker tag命令 [root@localhost ~]# docker tag -h Flag shorthand -h has been deprecated, please use --help Usage: docker tag SOURCE_IMAGE[:TAG指定鏡像] TARGET_IMAGE(倉庫名稱)[:TAG指定標簽名稱] 如果一個鏡像已經存在標簽的話,可以為一個鏡像打多個標簽 Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE [root@localhost ~]# docker tag 25a0f192ba8a localhost/httpd:v0.0.1 這個在沒有倉庫的時候我們只能去image id指定 [root@localhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE localhost/httpd v0.0.1 25a0f192ba8a 6 minutes ago 1.2MB busybox latest d8233ab899d4 4 weeks ago 1.2MB nginx 1.14-alpine 66952fd0a8ef 6 weeks ago 16MB [root@localhost ~]#可以看到我們已經給之前的鏡像打上了標簽和所屬倉庫信息 [root@localhost ~]# docker tag localhost/httpd:v0.0.1 localhost/httpd:v0.0.2 如果剛才的鏡像有修改了相關的內容,迭代了,我們可以在原有的鏡像的基礎上重新打一個標簽,標記為迭代的版本0.0.2 [root@localhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE localhost/httpd v0.0.1 25a0f192ba8a 9 minutes ago 1.2MB localhost/httpd v0.0.2 25a0f192ba8a 9 minutes ago 1.2MB busybox latest d8233ab899d4 4 weeks ago 1.2MB nginx 1.14-alpine 66952fd0a8ef 6 weeks ago 16MB [root@localhost ~]#除了tag不一樣之外,其它信息均為一致。 [root@localhost ~]# docker image rm localhost/httpd:v0.0.2 迭代的標簽內容寫錯了,我們就可以通過rm刪除鏡像,注意這個刪除不是真正刪除鏡像,只是刪除了標簽而已。 Untagged: localhost/httpd:v0.0.2 [root@localhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE localhost/httpd v0.0.1 25a0f192ba8a 14 minutes ago 1.2MB busybox latest d8233ab899d4 4 weeks ago 1.2MB nginx 1.14-alpine 66952fd0a8ef 6 weeks ago 16MB [root@localhost ~]# docker run --name box002 -it localhost/httpd:v0.0.1 / # ls bin data dev etc home proc root sys tmp usr var / # cat /data/html/index.html hello busybox html.server / # 可以看到我們制作的內容已經保存到了新的鏡像當中。
我們的容器一定是基於某個鏡像啟動的,我們已經通過原始的鏡像創建了新的鏡像,我們希望這個新的鏡像啟動的時候默認運行的命令不再是原來鏡像上的命令,當然我們專門指導鏡像原始的命令是什么了,可以使用之前的docker inspect busybox來查看cmd標記。

[root@localhost ~]# docker inspect busybox ........ "Cmd": [ "/bin/sh", 我們可以看到默認運行的是bin/sh "-c", "#(nop) ", "CMD [\"sh\"]" 默認的命令是sh ], ........ [root@localhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE localhost/httpd v0.0.1 25a0f192ba8a 32 minutes ago 1.2MB busybox latest d8233ab899d4 4 weeks ago 1.2MB nginx 1.14-alpine 66952fd0a8ef 6 weeks ago 16MB [root@localhost ~]# docker inspect localhost/httpd:v0.0.1 查看我們剛才制作的鏡像默認使用的命令 ...... "Cmd": [ "sh" ], ........
如果是我們要修改原有基礎鏡像的默認命令也是使用到docker commit命令,加上參數-c。

[root@localhost ~]# docker commit -h Flag shorthand -h has been deprecated, please use --help Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]] Create a new image from a container's changes Options: -a, --author string Author (e.g., "John Hannibal Smith <hannibal@a-team.com>") 加上作者信息 -c, --change list Apply Dockerfile instruction to the created image 修改原有鏡像的命令 -m, --message string Commit message -p, --pause Pause container during commit (default true) [root@localhost ~]# docker commit -a "docker <docker@dockerhub.cn>" -c 'CMD(這里的參數就是我們剛才inspect看到的參數) ["/bin/httpd(文件執行的位置,使用linux下which命令就能查到)","-f(前台運行)","-h(網頁的目錄)","/data/html(網頁目錄的路徑)"]' -p(在制作的時候暫停) box003 (基於制作的原鏡像名)localhost/httpd:v0.0.3(新倉庫和標簽名) sha256:fb4677ee396277ef09af92349d011376805bda1be1660004560656e9d0f65d3d [root@localhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE localhost/httpd v0.0.3 fb4677ee3962 3 minutes ago 1.2MB localhost/httpd v0.0.2 6f7c863d7eb0 17 minutes ago 1.2MB localhost/httpd v0.0.1 25a0f192ba8a About an hour ago 1.2MB busybox latest d8233ab899d4 4 weeks ago 1.2MB nginx 1.14-alpine 66952fd0a8ef 6 weeks ago 16MB [root@localhost ~]#版本3的鏡像已經制作完成 [root@localhost ~]# docker run --name httpd0.2 localhost/httpd:v0.0.2 我們把新鏡像啟動起來 [root@localhost ~]# docker inspect httpd0.2 查看此時的cmd信息 ........ "Cmd": [ "/bin/httpd", "-f", "-h", "/data/html" 可以看到原始的命令已經被修改成我們指定的命令了 ], "Gateway": "172.17.0.1", "IPAddress": "172.17.0.3",可以看到地址信息 ......... [root@localhost ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fc4b5604dd39 localhost/httpd:v0.0.2 "/bin/httpd -f -h /d…(可以看到運行的命令就和box001是不一樣的,不再是原始的sh了)" 3 minutes ago Up 3 minutes httpd0.2 7a60a03e9f3f busybox "sh" 2 hours ago Exited (0) 3 minutes ago box001 32ecea10ad3e nginx:1.14-alpine "nginx -g 'daemon of…" 2 days ago Exited (0) 2 days ago nginx 04e64051bd39 nginx:1.14-alpine "nginx -g 'daemon of…" 3 days ago Exited (0) 3 days ago nginx001 [root@localhost ~]# curl 172.17.0.3 直接訪問這個地址提供的信息 hello busybox html.server v0.02 [root@localhost ~]#
我們可以使用commit命令來快速創建一個屬於自己的鏡像。
當然我們自己制作的鏡像也可以推送到dockerhub上用於保存,在dockerhub上你可以選擇鏡像是公開還是私有,在推送的時候我們選擇docker push命令。

[root@localhost ~]# docker push -h Flag shorthand -h has been deprecated, please use --help Usage: docker push [OPTIONS] NAME(名字)[:TAG標簽] Push an image or a repository to a registry Options: --disable-content-trust Skip image signing (default true) [root@localhost ~]#這個推送的話可能需要登錄 登錄的話使用dokcer login [root@localhost ~]# docker login -h Flag shorthand -h has been deprecated, please use --help Usage: docker login [OPTIONS] [SERVER] 這個登錄參數里面如果是dockerhub的話[SERVER]參數是不用指的,如果是別的服務器就需要指定。 Log in to a Docker registry Options: -p, --password string Password --password-stdin Take the password from stdin -u, --username string Username [root@localhost ~]# docker login -u ppcserver Password: Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: TLS handshake timeout [root@localhost ~]# docker login -u ppcserver 登錄dockerhub 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 返回登錄成功 在push之前還需要注意的一個點就是你把本地的鏡像推送到dockerhub上的時候需要和dockerhub上的倉庫名保持一致,不然是不能推送的。 [root@localhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE ppcserver/httpd0.2 (本地的倉庫名稱需要和dockerhub上的保持一致) v0.0.2 d82b7aeb082a 34 seconds ago 1.2MB localhost/httpd v0.0.1 25a0f192ba8a 2 hours ago 1.2MB busybox latest d8233ab899d4 4 weeks ago 1.2MB nginx 1.14-alpine 66952fd0a8ef 6 weeks ago 16MB [root@localhost ~]# docker push ppcserver/httpd0.2:v0.0.2 The push refers to repository [docker.io/ppcserver/httpd0.2] acae7b22ffb2: Pushed adab5d09ba79: Mounted from library/busybox v0.0.2: digest: sha256:a1a1a6e9b46683c7f948861b898febb8eb5d3fd76c6cb4053566d9e79b489761 size: 734 [root@localhost ~]#可以看到push成功,可以上dockerhub上查看鏡像的信息 我們將本地的鏡像刪除,去dockerhub上把我們剛才推送的鏡像給拉下來 [root@localhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE localhost/httpd v0.0.1 25a0f192ba8a 2 hours ago 1.2MB busybox latest d8233ab899d4 4 weeks ago 1.2MB nginx 1.14-alpine 66952fd0a8ef 6 weeks ago 16MB [root@localhost ~]# docker pull ppcserver/httpd0.2:v0.0.2 v0.0.2: Pulling from ppcserver/httpd0.2 697743189b6d: Already exists a629bcaab0fe: Pull complete Digest: sha256:a1a1a6e9b46683c7f948861b898febb8eb5d3fd76c6cb4053566d9e79b489761 Status: Downloaded newer image for ppcserver/httpd0.2:v0.0.2 [root@localhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE ppcserver/httpd0.2 v0.0.2 d82b7aeb082a 12 minutes ago 1.2MB localhost/httpd v0.0.1 25a0f192ba8a 2 hours ago 1.2MB busybox latest d8233ab899d4 4 weeks ago 1.2MB nginx 1.14-alpine 66952fd0a8ef 6 weeks ago 16MB [root@localhost ~]#可以看到我們又將我們的鏡像pull下來了
如果你是要推送到像阿里雲這樣的鏡像倉庫的時候需要帶上服務器倉庫的地址,在dockerhub上下載的時候也需要注意,盡量不要下載倉庫里面用戶的鏡像,之前有很多鏡像下載下來之后導致服務器被肉雞使用。
C:鏡像的導入和導出:
我們很多時候都是用於測試使用,當在一台機器上制作完一個鏡像,想放到測試機器上進行測試,這個時候push的方式很麻煩, 這種情況下我們就可以把已有的鏡像打包,放到另外一台機器裝一下運行即可。我們需要使用到的兩個命令:
1:docker save(保存打包文件)

[root@localhost ~]# docker save -h Flag shorthand -h has been deprecated, please use --help Usage: docker save [OPTIONS] IMAGE [IMAGE...] save一次可以打包多個鏡像 Save one or more images to a tar archive (streamed to STDOUT by default) Options: -o, --output string Write to a file, instead of STDOUT -o參數指定打包之后存在那個文件中 [root@localhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE ppcserver/httpd0.2 v0.0.2 d82b7aeb082a 44 minutes ago 1.2MB localhost/httpd v0.0.1 25a0f192ba8a 3 hours ago 1.2MB busybox latest d8233ab899d4 4 weeks ago 1.2MB nginx 1.14-alpine 66952fd0a8ef 6 weeks ago 16MB [root@localhost ~]# docker save -o /home/myimages.gz ppcserver/httpd0.2:v0.0.2 localhost/httpd:v0.0.1 我們將版本1和版本2直接打包放在home下取名為myimages,而且並壓縮成gz的格式 [root@localhost ~]# cd /home/ [root@localhost home]# ls docker myimages.gz [root@localhost home]#
2:docker load從打包文件直接安裝

[root@localhost /]# docker load -h Flag shorthand -h has been deprecated, please use --help Usage: docker load [OPTIONS] Load an image from a tar archive or STDIN Options: -i, --input string Read from tar archive file, instead of STDIN 指定從那個文件來load -q, --quiet Suppress the load output [root@localhost /]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE busybox latest d8233ab899d4 4 weeks ago 1.2MB nginx 1.14-alpine 66952fd0a8ef 6 weeks ago 16MB [root@localhost home]# docker load -i myimages.gz acae7b22ffb2: Loading layer [==================================================>] 5.12kB/5.12kB Loaded image: ppcserver/httpd0.2:v0.0.2 751d8660062f: Loading layer [==================================================>] 5.12kB/5.12kB Loaded image: localhost/httpd:v0.0.1 [root@localhost home]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE ppcserver/httpd0.2 v0.0.2 d82b7aeb082a About an hour ago 1.2MB localhost/httpd v0.0.1 25a0f192ba8a 4 hours ago 1.2MB busybox latest d8233ab899d4 4 weeks ago 1.2MB nginx 1.14-alpine 66952fd0a8ef 6 weeks ago 16MB [root@localhost home]#
docker save和docker load兩種方式也存在一個問題就是你使用run或者pull的時候,因為你本地沒有鏡像,還是回去鏡像本應該所屬的位置去拉鏡像下來,所以使用這種方式你本地還是得有鏡像。
除了save和load還有export和import。
♣四:容器的虛擬化網絡
A:容器虛擬化網絡基礎:
在文章的最開始我們就介紹了容器的6種命名空間,都是用於隔離技術而存在的,那么容器的虛擬化網絡在內核很早的時候已經支持,主要用於隔離協議棧和網路設備。
假如我們的服務器只有一塊物理網卡,此時在這台機器上創建了很多容器,遠遠大於網卡設備的數量,這個時候容器運行的鏡像需要進行通訊怎么辦,所以需要使用到虛擬網卡來保證服務的正常通訊。
在我們常用的vmwar workstation中我們可以使用虛擬網卡的功能來虛擬出很多網卡設備來保證正常的通訊,而在linux內核中能完全支持二層三層設備的模擬,二層設備就包含我們的網卡,二層設備能封裝物理報文在各個設備之間進行報文轉發,而且這種網絡很獨特,每一張網絡設備是成對出現的,這就好比一根網線的兩頭,能連接虛擬機和交換機之上一樣,而這種網卡模擬在我們內核就原生支持。在linux上我們可以通過ovs(openvswitch)能工具來模擬交換機等設備。
有了這種工具和內核的支持,我們在一個平台上搭建不不同的容器,然后通過內核和工具,將網絡一頭接在容器這邊,一頭接在交換機之上,運用相同的概念,我們就能完容器之間的通訊。這個是簡單的網絡形式,如果是我在一個平台之上有兩個交換機,而且在不同網段怎么辦,這個時候也是需要虛擬網絡,在通過工具模擬路由器,通過虛擬網路把兩邊的交換機接到路由器上,這樣稍微復雜的網絡文明也能實現,但是路由器屬於三層設備,要想使用就需要使用一個單獨的容器(用戶空間)來完成,如果更加復雜的網絡就不建議使用這種技術了,因為很容易出現網絡風暴,而且效率不高。
我們發現容器在需要通訊的時候因為要不斷的考慮網絡形態來應對復雜的容器場景,這個里面很容易出現問題,因此我們在面對這種容器網絡的情況下就可以使用overlay network(疊加網絡技術),這種技術就是在原有的物理機網卡之上建立一個虛擬的網絡橋,上層的容器的通訊就借助我們的物理網絡來完成報文的隧道轉發,可以完成跨設備容器之間的通訊。
假如server1和server9在兩台不通的物理服務器上,而且不在同一網段,這個時候當server1需要訪問server9的時候,就需要把報文信息發送給網絡橋並封裝報文信息,在由網絡橋轉發給物理網卡,server1的物理網卡在此時肯定不會知道server9所在的地址,但是server1知道server9所在的物理網卡地址,此時server1又將已經封裝的報文信息二次封裝加上發給server9所在的物理網卡,server9在解開封裝的報文之后發現是給到server9網絡橋之上的機器的,這個時候就再轉發給server9所在的虛擬機器。
B:docker的網絡形式:
docker在安裝之后默認提供了三種網絡:
1:bridge(橋接式網絡,此處的橋接是net橋,bridge會在本機創建一個軟交換機,就是我們之前看到的docker 0,這個docker 0可以充當交換機之外還能充當我們的網卡設備,不給地址的時候就是交換機,給了地址既是交換機又是網絡設備)
當我們啟動一個容器,就會創建一個veth開頭后面隨機的網卡信息:

[root@localhost ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a71b4f12bed7 ppcserver/httpd0.2:v0.0.2 "/bin/httpd -f -h /d…" About a minute ago Up About a minute httpd0.2 32ecea10ad3e nginx:1.14-alpine "nginx -g 'daemon of…" 3 days ago Up 4 minutes 80/tcp nginx 04e64051bd39 nginx:1.14-alpine "nginx -g 'daemon of…" 3 days ago Exited (0) 3 days ago nginx001 [root@localhost ~]#我們現在處於運行的網卡有兩個 [root@localhost ~]# ifconfig veth11541d3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet6 fe80::d818:72ff:fe50:8e8e prefixlen 64 scopeid 0x20<link> ether da:18:72:50:8e:8e txqueuelen 0 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 22 bytes 2422 (2.3 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 veth56e9b13: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet6 fe80::fcff:3eff:fed0:c335 prefixlen 64 scopeid 0x20<link> ether fe:ff:3e:d0:c3:35 txqueuelen 0 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 8 bytes 656 (656.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 會發現多處了這兩張網卡信息,而且這個網卡都是成對出現的,一個在容器,一個在軟橋,就相當於一根網線連接的設備的兩頭。 上面說了docker 0在沒有配置網絡的時候就是交換機,我們可以通過brctl show(注意這個命令是要安裝bridge-utils包的)命令來看下交換機上是不是有我們ifconfig的網卡,先找到接在交換機上的網卡信息。 [root@localhost ~]# brctl show bridge name bridge id STP enabled interfaces docker0 8000.0242bb636b7b no veth11541d3 veth56e9b13 virbr0 8000.525400fb0995 yes virbr0-nic [root@localhost ~]# ifconfig veth11541d3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet6 fe80::d818:72ff:fe50:8e8e prefixlen 64 scopeid 0x20<link> ether da:18:72:50:8e:8e txqueuelen 0 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 22 bytes 2422 (2.3 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 veth56e9b13: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet6 fe80::fcff:3eff:fed0:c335 prefixlen 64 scopeid 0x20<link> ether fe:ff:3e:d0:c3:35 txqueuelen 0 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 8 bytes 656 (656.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 可以看到軟橋上正好存在了兩張網卡信息和我們ifconfig對的上,那么另外一般是存在容器里面的,我們先通過ip link show先看下另外一半的簡要信息,要和我們現在的看到這半邊是銜接上的。 [root@localhost ~]# ip link show ..... 7: veth11541d3@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default link/ether da:18:72:50:8e:8e brd ff:ff:ff:ff:ff:ff link-netnsid 0 9: veth56e9b13@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default link/ether fe:ff:3e:d0:c3:35 brd ff:ff:ff:ff:ff:ff link-netnsid 1 ..... veth56e9b13@if8網卡信息的兩邊是通過@符號來分割的,我們可以看到接在軟橋上的網卡信息,還能看到另外的半邊的信息,雖然就就顯示了一部分。 我們使用iptables的時候可以看到這整個nat過程都是自動生成的 Chain POSTROUTING (policy ACCEPT 255 packets, 17822 bytes) pkts bytes target prot opt in out source destination 0 0 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0 2 267 RETURN all -- * * 192.168.122.0/24 224.0.0.0/24 0 0 RETURN all -- * * 192.168.122.0/24 255.255.255.255 0 0 MASQUERADE tcp -- * * 192.168.122.0/24 !192.168.122.0/24 masq ports: 1024-65535 0 0 MASQUERADE udp -- * * 192.168.122.0/24 !192.168.122.0/24 masq ports: 1024-65535 0 0 MASQUERADE all -- * * 192.168.122.0/24 !192.168.122.0/24 437 30088 POSTROUTING_direct all -- * * 0.0.0.0/0 0.0.0.0/0 437 30088 POSTROUTING_ZONES_SOURCE all -- * * 0.0.0.0/0 0.0.0.0/0 437 30088 POSTROUTING_ZONES all -- * * 0.0.0.0/0 0.0.0.0/0
2:host (讓容器使用宿主機的網絡名稱空間)
3:none (是沒有網絡的,容器里面存在這種網絡的意義就是臨時處理下數據,處理完成存儲到我們的硬盤上,方便其它的容器讀取)
docker一共有四種網絡形式:
1:closed container(封閉式網絡,指的就是我們上面的none,只有lo接口)
2:bridged container (橋接式網絡,默認地址是172.17.0.1地址,稱作橋接式網絡或者nat網絡)
3:joined container (聯盟式網絡,讓容器之間部分名稱空間是隔離的,但是在uts,nat,ipc共享同一組,然后容器之間就能通訊了)
4:open container (開放式網路,直接共享物理機的網絡名稱空間,也就意味着物理機能看到的網絡,容器也能看到)
C:docker網絡的相關操作:
知道docker能夠使用的網絡形式之后,我們就可以使用命令來指定查看運行中國容器使用的網絡是屬於哪種網絡形式

[root@localhost ~]# docker container inspect httpd0.2 ......... "Networks": { "bridge": { 使用的默認的bridge網絡 "IPAMConfig": null, "Links": null, "Aliases": null, "NetworkID": "4e1f3c27c35496f43410401bc6de83c12f00a48ffae8e4351017a815435db40f", "EndpointID": "68676ed0886db79ff05cdb6a630083daf882bf914b7902b5b767974e14ebbe53", "Gateway": "172.17.0.1", 容器的網關 "IPAddress": "172.17.0.2", 地址 "IPPrefixLen": 16, "IPv6Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "MacAddress": "02:42:ac:11:00:02", 當前容器的mac地址 "DriverOpts": null ..........
在開放式網絡中,docker的網絡名稱空間,uts,ipc是能被容器共享的,我們可以手動通過ip命令操作我們名稱空間,ip命令所屬與iproute包組。

[root@localhost ~]# ip Usage: ip [ OPTIONS ] OBJECT { COMMAND | help } ip [ -force ] -batch filename where OBJECT := { link | address | addrlabel | route | rule | neigh | ntable | tunnel | tuntap | maddress | mroute | mrule | monitor | xfrm | netns(我們可以看到ip能夠操作網絡名稱空間) | l2tp | fou | macsec | tcp_metrics | token | netconf | ila | vrf } OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] | -h[uman-readable] | -iec | -f[amily] { inet | inet6 | ipx | dnet | mpls | bridge | link } | -4 | -6 | -I | -D | -B | -0 | -l[oops] { maximum-addr-flush-attempts } | -br[ief] | -o[neline] | -t[imestamp] | -ts[hort] | -b[atch] [filename] | -rc[vbuf] [size] | -n[etns] name | -a[ll] | -c[olor]} [root@localhost ~]# ip netns help Usage: ip netns list 列出網絡名稱空間 ip netns add NAME 添加網絡名稱空間 ip netns set NAME NETNSID 設置網絡名稱空間的sid ip [-all] netns delete [NAME] ip netns identify [PID] ip netns pids NAME ip [-all] netns exec [NAME] cmd ... 在網絡命令空間內執行相關的命令 ip netns monitor ip netns list-id ip link挪動網絡名稱空間 當我們通過ip命令管理網絡名稱空間的時候,只有網絡名稱空間是隔離的,其它的都是處於同一層上的,這和docker的網絡名稱空間有所不一樣的地方。

[root@localhost ~]# ip netns add ip001 通過add添加網絡名稱空間 [root@localhost ~]# ip netns add ip002 [root@localhost ~]# ip netns list ip002 ip001 [root@localhost ~]# ip netns exec ip001 ifconfig -a lo: flags=8<LOOPBACK> mtu 65536 loop txqueuelen 1000 (Local Loopback) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 我們在001里面執行命令來查看網卡信息,結果是沒有的,因為我們單獨指定創建網卡設備 ,沒有指定的情況下默認就是lo接口 我們還可以通過ip命令創建虛擬網卡對,將網卡的兩端分別綁定在ip001和ip002上。 [root@localhost ~]# ip link add(添加) name veth0.1(名字是veth0.1) type veth(虛擬網卡需要使用veth格式) peer(指定另外一半) name veth0.2(名字是veth0.2) [root@localhost ~]# ip link show 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 link/ether 00:0c:29:00:75:55 brd ff:ff:ff:ff:ff:ff 3: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default qlen 1000 link/ether 52:54:00:fb:09:95 brd ff:ff:ff:ff:ff:ff 4: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast master virbr0 state DOWN mode DEFAULT group default qlen 1000 link/ether 52:54:00:fb:09:95 brd ff:ff:ff:ff:ff:ff 5: veth0.2@veth0.1(可以看到名稱空間里面各自的一半就是我們指定的內容): <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether e2:1f:c1:54:26:18 brd ff:ff:ff:ff:ff:ff 6: veth0.1@veth0.2: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether b6:cb:2e:75:cd:ec brd ff:ff:ff:ff:ff:ff [root@localhost ~]#這樣在我們宿主機上就有了一對網絡設備,但是這種方式創建的是沒有激活的,我們將網絡設備的一邊綁定到我們剛才創建好的ip001上。 [root@localhost ~]# ip link set dev veth0.2 netns ip001 使用set參數將0.2添加到netns虛擬網絡名稱空間ip001里面 [root@localhost ~]# ip link show 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 link/ether 00:0c:29:00:75:55 brd ff:ff:ff:ff:ff:ff 3: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default qlen 1000 link/ether 52:54:00:fb:09:95 brd ff:ff:ff:ff:ff:ff 4: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast master virbr0 state DOWN mode DEFAULT group default qlen 1000 link/ether 52:54:00:fb:09:95 brd ff:ff:ff:ff:ff:ff 6: veth0.1@if5(可以看到veth0.2設備已經被挪走了): <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether b6:cb:2e:75:cd:ec brd ff:ff:ff:ff:ff:ff link-netnsid 0 [root@localhost ~]# ip netns exec ip001 ifconfig -a ip001就多了一個veth0.2的網絡設備 lo: flags=8<LOOPBACK> mtu 65536 loop txqueuelen 1000 (Local Loopback) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 veth0.2: flags=4098<BROADCAST,MULTICAST> mtu 1500 ether e2:1f:c1:54:26:18 txqueuelen 1000 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 [root@localhost ~]# ip netns exec ip001 ip link set dev veth0.2 name eth2 我們還可以給網卡設備名改名 [root@localhost ~]# ip netns exec ip001 ifconfig -a eth2: flags=4098<BROADCAST,MULTICAST> mtu 1500 ether e2:1f:c1:54:26:18 txqueuelen 1000 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 lo: flags=8<LOOPBACK> mtu 65536 loop txqueuelen 1000 (Local Loopback) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 [root@localhost ~]# ifconfig veth0.1 2.4.6.8./24 up 通過ifconfig命令給設備veth0.1設置ip為2.4.6.8./24位掩碼並激活 [root@localhost ~]# ifconfig ..... veth0.1: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 inet 2.4.6.8 netmask 255.255.255.0 broadcast 2.4.6.255 ether b6:cb:2e:75:cd:ec txqueuelen 1000 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 ...... [root@localhost ~]# ip netns exec ip001 ifconfig eth2 2.4.6.100/24 up我們通過命令將網絡名稱空間里面的ip001也給指定ip並啟動起來 [root@localhost ~]# ip netns exec ip001 ifconfig -a eth2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 2.4.6.100 netmask 255.255.255.0 broadcast 2.4.6.255 inet6 fe80::e01f:c1ff:fe54:2618 prefixlen 64 scopeid 0x20<link> ether e2:1f:c1:54:26:18 txqueuelen 1000 (Ethernet) RX packets 14 bytes 1766 (1.7 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 8 bytes 656 (656.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 lo: flags=8<LOOPBACK> mtu 65536 loop txqueuelen 1000 (Local Loopback) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 [root@localhost ~]# ping 2.4.6.100 PING 2.4.6.100 (2.4.6.100) 56(84) bytes of data. 64 bytes from 2.4.6.100: icmp_seq=1 ttl=64 time=0.103 ms 64 bytes from 2.4.6.100: icmp_seq=2 ttl=64 time=0.117 ms ^Z [1]+ 已停止 ping 2.4.6.100 [root@localhost ~]# 可以看到我們的宿主機和虛擬網絡名稱空間里面的網絡就可以通訊了 [root@localhost ~]# ip link set dev veth0.1 netns ip002 我們將另外一半挪到另外一半名稱空間去 [root@localhost ~]# ip netns exec ip002 ifconfig -a lo: flags=8<LOOPBACK> mtu 65536 loop txqueuelen 1000 (Local Loopback) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 veth0.1: flags=4098<BROADCAST,MULTICAST> mtu 1500 ether b6:cb:2e:75:cd:ec txqueuelen 1000 (Ethernet) RX packets 12 bytes 936 (936.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 18 bytes 2046 (1.9 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 [root@localhost ~]#這樣挪進來默認是沒有被激活的 [root@localhost ~]# ip netns exec ip002 ip link set dev veth0.1 name eth1 [root@localhost ~]# ip netns exec ip002 ifconfig eth1 2.4.6.200/24 up [root@localhost ~]# ip netns exec ip002 ifconfig -a eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 2.4.6.200(可以看到ip002也有了ip地址) netmask 255.255.255.0 broadcast 2.4.6.255 inet6 fe80::b4cb:2eff:fe75:cdec prefixlen 64 scopeid 0x20<link> ether b6:cb:2e:75:cd:ec txqueuelen 1000 (Ethernet) RX packets 12 bytes 936 (936.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 26 bytes 2702 (2.6 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 lo: flags=8<LOOPBACK> mtu 65536 loop txqueuelen 1000 (Local Loopback) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 [root@localhost ~]# ip netns exec ip002 ping 2.4.6.100 我們在ip002里面pingip001的地址是可以通訊的 PING 2.4.6.100 (2.4.6.100) 56(84) bytes of data. 64 bytes from 2.4.6.100: icmp_seq=1 ttl=64 time=0.163 ms 64 bytes from 2.4.6.100: icmp_seq=2 ttl=64 time=0.113 ms ^Z [4]+ 已停止 ip netns exec ip002 ping 2.4.6.100 [root@localhost ~]# ip netns exec ip001 ping 2.4.6.200 反過來也是能通訊的 PING 2.4.6.200 (2.4.6.200) 56(84) bytes of data. 64 bytes from 2.4.6.200: icmp_seq=1 ttl=64 time=0.057 ms 64 bytes from 2.4.6.200: icmp_seq=2 ttl=64 time=0.133 ms ^Z [5]+ 已停止 ip netns exec ip001 ping 2.4.6.200 [root@localhost ~]#

[root@localhost ~]# docker run --name box001 --network(歐陽--network參數指定網絡是none) none -it --rm (在容器退出就刪除,此處是為了方便測試)busybox:latest / # ifconfig lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) / #可以看到就屬於封閉式網絡

在主機與主機之間通訊很多時候會用到主機名來通訊 [root@localhost ~]# docker run --name box001 -it --rm busybox:latest / # hostname 781e119ed613 / # 可以看待當前容器的主機名是一個自動生成的名稱,並且這個名稱就是我們容器的id [root@localhost ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 781e119ed613 busybox:latest "sh" 2 minutes ago Up 2 minutes box001 [root@localhost ~]# docker run --name box001 -it -h v1.1host --rm busybox:latest 我們也可以在創建容器的時候用-h指定主機名 / # hostname v1.1host / # 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.17.0.2 v1.1host v1 當有了主機名我們就可以通過主機名來解析 / # cat /etc/resolv.conf # Generated by NetworkManager search localdomain nameserver 192.168.181.2 可以看到當前容器指定的dns是192.168.181.2,這個地址是我們宿主機的dns地址 這樣我們的容器就能通過宿主機的dns順利的解析到公網的地址信息 / # nslookup -type=A www.taobao.com Server: 192.168.181.2 Address: 192.168.181.2:53 Non-authoritative answer: www.taobao.com canonical name = www.taobao.com.danuoyi.tbcache.com Name: www.taobao.com.danuoyi.tbcache.com Address: 103.43.210.219 Name: www.taobao.com.danuoyi.tbcache.com Address: 103.43.210.61 Name: www.taobao.com.danuoyi.tbcache.com Address: 103.43.210.87 Name: www.taobao.com.danuoyi.tbcache.com Address: 103.43.210.186 可以看到能夠正常解析公網的域名信息 [root@localhost ~]# docker run --name box001 -it -h v1.1host --dns 123.123.123.123 --rm busybox:latest 如果我們不想使用宿主機的dns地址,想自己指定的話也可以自己去指定 / # cat /etc/resolv.conf search localdomain nameserver 123.123.123.123 / # 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.17.0.2 v1.1host v1 / # nslookup -type=A www.baidu.com Server: 123.123.123.123 Address: 123.123.123.123:53 Non-authoritative answer: www.baidu.com canonical name = www.a.shifen.com Name: www.a.shifen.com Address: 119.75.217.109 Name: www.a.shifen.com Address: 119.75.217.26 / # [root@localhost ~]# docker run --name box001 -it -h v1.1host --dns 123.123.123.123 --add-host www.baidu.com:10.10.10.10 --rm busybox:latest / # cat /etc/hosts 我們還可以在docker啟動的時候注入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 10.10.10.10 www.baidu.com 172.17.0.2 v1.1host v1 / #
當我們創建了一個例如nginx這樣的服務,這樣的服務是需要發布到互聯網的,這個時候我們就需要使用到開放式網絡,當我們容器運行的nginx監聽的是80端口,這個時候我們就需要把80端口暴露在我們物理機的網卡ip地址之上的某個端口。

我們通過啟動容器的時候添加選項-p指定容器端口,而這個端口會被映射到主機所有地址的一個動態端口。 [root@localhost ~]# docker run --name web1 --rm -p 80 ppcserver/httpd0.2:v0.0.2 啟動容器服務 因為現在走的net橋,指定容器端口是80,現在外面要訪問肯定是走宿主機的本機ip,還有就是現在端口在宿主機是動態的,我們可以通過docker port web1來查看映射的動態端口 [root@localhost ~]# docker port web1 80/tcp -> 0.0.0.0:32768 這個0.0.0.0就代表了宿主機的所有地址 [root@localhost ~]# iptables -t nat -vnL 這個地址映射完全是自動的,我們通過iptables就能看整個映射過程,而且整個net規則是通過我們-p選項自動產生的。 當我們停止容器的運行,這條規則也就自動刪除了 ...... Chain DOCKER (2 references) pkts bytes target prot opt in out source destination 0 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0 0 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:32768 to:172.17.0.2:80 ....... 可以看到172.17.0.2:80端口被映射成了32768端口,我們通過瀏覽器直接就可以訪問我們宿主機的32678端口就能看到網頁內容 如果我們想固定下來使用宿主機的一個地址,不希望被映射到宿主機的所有地址之上,就可以使用-p 加上指定ip [root@localhost ~]# docker run --name web1 -p 192.168.181.132::80 --rm ppcserver/httpd0.2:v0.0.2 注意指定ip后面的兩個冒號是必須的。 [root@localhost ~]# docker port web1 80/tcp -> 192.168.181.132:32768 可以看見現在就是指定的ip地址的動態端口了 我們能不能使用宿主機的所有ip但是出去的端口我要求是固定的80,也是可以實現的。 [root@localhost ~]# docker run --name web1 -p 80:80 --rm ppcserver/httpd0.2:v0.0.2 注意,-p后面沒有接ip地址就代表所有宿主機的ip地址,80:80之間只有一個冒號。 [root@localhost ~]# docker port web1 80/tcp -> 0.0.0.0:80 那要既要指定ip也要指定端口也就變得很簡單了 [root@localhost ~]# docker run --name web1 -p 192.168.181.132:80:80 --rm ppcserver/httpd0.2:v0.0.2 [root@localhost ~]# docker port web1 80/tcp -> 192.168.181.132:80 docker 開放式網絡還有一個-P參數,這個參數會將容器所有計划要暴露的端口全部應設置主機端口 [root@localhost ~]# docker run --name web1 -P --expose 80 --expose 888 --rm ppcserver/httpd0.2:v0.0.2 [root@localhost ~]# docker port web1 80/tcp -> 0.0.0.0:32770 888/tcp -> 0.0.0.0:32769 [root@localhost ~]# docker run --name web1 -P --expose 80 --expose 888 --expose 808 --expose 88 --rm ppcserver/httpd0.2:v0.0.2 [root@localhost ~]# docker port web1 80/tcp -> 0.0.0.0:32774 808/tcp -> 0.0.0.0:32772 88/tcp -> 0.0.0.0:32773 888/tcp -> 0.0.0.0:32771 [root@localhost ~]#

[root@localhost ~]# docker run --name box1 -it busybox:latest 我們開啟一個容器,發現會自動的幫忙分派一個ip / # ifconfig eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:02 inet addr:172.17.0.2 Bcast:172.17.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:6 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:516 (516.0 B) TX bytes:0 (0.0 B) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) / # [root@localhost ~]# docker run --name box2 -it busybox:latest 再創建一個容器,發現又自動生成了一個ip / # ifconfig eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:03 inet addr:172.17.0.3 Bcast:172.17.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:6 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:516 (516.0 B) TX bytes:0 (0.0 B) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) / # 我們發現每次容器啟動會自動產生一個不一樣的ip [root@localhost ~]# docker run --name box2 --network container:box1 -it --rm busybox:latest 我們在啟動容器的時候直接指定共用那個容器的網絡名稱空間,這樣我們就能獲得和被指定網絡名稱空間一樣的ip / # ifconfig eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:02 inet addr:172.17.0.2 Bcast:172.17.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:8 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:656 (656.0 B) TX bytes:0 (0.0 B) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) / # box1 / # mkdir /data 我們在第一個容器里面創建目錄, / # ls bin data dev etc home proc root sys tmp usr var / # box2 在另外一個容器是不能看到的 / # ls bin dev etc home proc root sys tmp usr var / # 我們在box2上個創建一個網頁 / # echo "hello busybox" > /home/index.html / # httpd -h /home/ / # netstat -anput 80 Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 :::80 :::* LISTEN 9/httpd / # 在box1上訪問 / # wget -O - -q 127.0.0.1 hello busybox / # 可以看到是能訪問的,說明他們共享了網絡名稱空間,但是不會共享其它的名稱空間。 既然我們創建的時候能共享容器的網絡名稱空間,那我們也能共享宿主機的網絡名稱空間。 [root@localhost ~]# docker run --name box3 --network host -it --rm busybox:latest / # ifconfig ...... ens33 Link encap:Ethernet HWaddr 00:0C:29:00:75:55 inet addr:192.168.181.132 Bcast:192.168.181.255 Mask:255.255.255.0 inet6 addr: fe80::d8dc:efca:4044:5802/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:6281 errors:0 dropped:0 overruns:0 frame:0 TX packets:4058 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:621103 (606.5 KiB) TX bytes:477461 (466.2 KiB) ...... 我們創建一個httpd的服務,看看是不是能通過瀏覽器訪問 / # echo "hello docker.server" > /home/index.html / # httpd -h /home/ / # netstat -anptu | grep 80 tcp 0 0 :::80 :::* LISTEN 8/httpd / # 我們通過瀏覽器就能訪問到我們剛才編輯的網頁內容了,這種看似和在宿主機上搭建網絡沒有什么區別,但是你會發現我要快速搭建httpd服務的時候,直接一條命令完事兒,比原始的操作簡單太多了。

我們在創建容器的時候一直都是使用的默認172.17.0網段,這個地址和網段是能調整的,這樣我們就能在以后不同的容器給到不同的默認網絡 這個調整默認ip是需要修改/etc/docker/daemon.json文件。 配置項如下: { 多項配置逗號分隔 "bip": "192.168.0.1/24", 指dokcer 0橋的ip地址和掩碼 "fixed-cidr": "10.10.10.0/16", "fixed-cidr-v6": "mut": "default-geteway": "192.168.0.1, 默認網關 ........ "dns": ["10.10.10.10","114.114.114.114"] dns不配置就是用戶宿主機的,dns最多給到3個,最少一個 ..... } [root@localhost ~]# cat /etc/docker/daemon.json { "registry-mirrors":["https://registry.docker-cn.com"], "bip": "192.168.0.1/24" 改下配置文件加上指定的默認網段 } [root@localhost ~]# docker run --name box1 -it --rm busybox:latest / # ifconfig eth0 Link encap:Ethernet HWaddr 02:42:C0:A8:00:02 inet addr:192.168.0.2 Bcast:192.168.0.255 Mask:255.255.255.0 可以看到默認地址就是我們指定的地址段了,至於其它的東西,docker會自動幫忙計算生成 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:5 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:426 (426.0 B) TX bytes:0 (0.0 B) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) / # 我們在另外一個docker默認是不能控制宿主機上另外一個docker容器的,因為每個docker指監聽了unix指定路徑下的sock文件,默認路徑在/var/run/docker.sock,如果我們要想其他用戶在外網正常訪問我們的docker容器,我們就需要使用-H參數 -H, --host list Daemon socket(s) to connect to ,如果我們沒有指定訪問的信息,默認就是走的本地的sock文件記錄的信息 這個時候我們需要先改daemon.json配置文件 "hosts": ["tcp://0.0.0.0:1221", "unix:///var/run/docker.sock"] 然后重啟docker,在容器1就能操作容器2。 在執行的時候要加-H --host(ip和端口)。 在docker還可以創建自動以的橋 我們在docker iofo查看信息的時候可以看到docker的網絡插件支持如下幾種 Network: bridge host macvlan null(封閉式網絡) overlay(疊加網絡) 我們在使用命令就可以創建自定義的橋bridge [root@localhost home]# docker network create -d bridge --subnet "192.168.12.1/16" --gateway "192.168.0.1" mybr1 6b16879aaff29aaf34ef1cbba51ac4c5eb4ae6f90624d0bd3c9a215bac28b2e3 [root@localhost home]# docker network ls NETWORK ID NAME DRIVER SCOPE a4ff9eacbd15 bridge bridge local 562cbe3d542c host host local 6b16879aaff2 mybr1 bridge local 30c6f39ae8be none null local [root@localhost home]#可以看到我們就創建了一個自定義的[root@localhost home]# docker network create -d bridge --subnet "192.168.12.1/16" --gateway "192.168.0.1" mybr1 6b16879aaff29aaf34ef1cbba51ac4c5eb4ae6f90624d0bd3c9a215bac28b2e3 [root@localhost home]# docker network ls NETWORK ID NAME DRIVER SCOPE a4ff9eacbd15 bridge bridge local 562cbe3d542c host host local 6b16879aaff2 mybr1 bridge local 30c6f39ae8be none null local [root@localhost home]# ifconfig br-6b16879aaff2: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 inet 192.168.0.1 netmask 255.255.0.0 broadcast 192.168.255.255 ether 02:42:0c:b3:a1:e1 txqueuelen 0 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 但是網絡接口的名稱是我們創建的時候生成的加密字符,后續可以通過ip link來調整名字即可 [root@localhost home]# docker run --name box1 -it --net mybr1 busybox:latest 啟動容器的時候直接指定網絡橋 Unable to find image 'busybox:latest' locally latest: Pulling from library/busybox 697743189b6d: Pull complete Digest: sha256:061ca9704a714ee3e8b80523ec720c64f6209ad3f97c0ff7cb9ec7d19f15149f Status: Downloaded newer image for busybox:latest / # ls bin dev etc home proc root sys tmp usr var / # ifconfig eth0 Link encap:Ethernet HWaddr 02:42:C0:A8:00:02 inet addr:192.168.0.2 Bcast:192.168.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:19 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:2212 (2.1 KiB) TX bytes:0 (0.0 B) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) 我們在宿主機開啟兩個容器,分別使用我們自定義的mybr1和默認的bridge。 [root@localhost home]# docker run --name box1 -it --net mybr1 busybox:latest Unable to find image 'busybox:latest' locally latest: Pulling from library/busybox 697743189b6d: Pull complete Digest: sha256:061ca9704a714ee3e8b80523ec720c64f6209ad3f97c0ff7cb9ec7d19f15149f Status: Downloaded newer image for busybox:latest / # ls bin dev etc home proc root sys tmp usr var / # ifconfig eth0 Link encap:Ethernet HWaddr 02:42:C0:A8:00:02 inet addr:192.168.0.2 Bcast:192.168.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:19 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:2212 (2.1 KiB) TX bytes:0 (0.0 B) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) / # [root@localhost ~]# docker run --name box2 -it --net bridge busybox:latest / # ifconfig eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:02 inet addr:172.17.0.2 Bcast:172.17.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:17 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:1963 (1.9 KiB) TX bytes:0 (0.0 B) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) / # ping 192.168.0.2 PING 192.168.0.2 (192.168.0.2): 56 data bytes ^Z[1]+ Stopped ping 192.168.0.2 現在我們用box2訪問box1的地址,因為屬於不通網段,看似是不能相互訪問的,我們可以通過把核心轉發功能開啟並關閉iptables阻斷虛擬橋之間訪問的規則,其實兩個容器之間是能訪問的 [root@localhost ~]# cat /proc/sys/net/ipv4/ip_forward 1 [root@localhost ~]# iptables -vnL Chain DOCKER-ISOLATION-STAGE-1 (1 references) pkts bytes target prot opt in out source destination 0 0 DOCKER-ISOLATION-STAGE-2 all -- br-6b16879aaff2 !br-6b16879aaff2 0.0.0.0/0 0.0.0.0/0 2 168 DOCKER-ISOLATION-STAGE-2 all -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0 0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 [root@localhost ~]# iptables -F box2訪問box1IP地址 PING 192.168.0.2 (192.168.0.2): 56 data bytes 64 bytes from 192.168.0.2: seq=0 ttl=63 time=0.248 ms 64 bytes from 192.168.0.2: seq=1 ttl=63 time=0.153 ms ^Z[2]+ Stopped ping 192.168.0.2 / # 發現可以通訊了
♣五:docker的存儲卷
A:docker的存儲卷介紹:
在文章的前面我們我們提到了docker的聯合掛載和分層構建技術,事實上docker在啟動的時候默認會產生一個讀寫層,如果用戶對運行中的容器修改了現有的一個以存在的文件,那改文件將會從讀寫層之下的只讀層復制一個文件到讀寫層,該文件的最初版本只讀版其實還在,只不過讀寫層中改文件的原始版本被影藏了,這種機制叫做寫時復制機制。
寫時復制這種機制在最上層讀寫層始終可見的就代表下層一定會有,但是讀寫層刪除的(標記為刪除,其實就是影藏了),下層並不會刪除,如果層級太多,這種機制其實效率並不高,假如我們使用了頻繁I/O的服務的時候,這種機制就會在服務自身的瓶頸之上又多了一層瓶頸,而且容器一旦被刪除,數據也就刪除了,務必會造成數據的丟失。
面對這種情況,我們勢必要繞過容器本身的文件系統帶來的特定機制,寫入數據在容器,存儲數據在宿主機上面,這種場景才是可取的,簡單理解就是我們在宿主機建立一個特定目錄,然后把容器里面的本來要存數據的目錄給綁定到外面宿主機的特定目錄即可。其實這種場景就像是掛載技術,讓容器可以直接訪問我宿主機的內容,同時宿主機也能給容器提供內容,二者是相互的。
這種場景不僅僅可以運用於容器和宿主機之間的數據讀寫,還可以通過宿主機的牽線搭橋,讓在同一個宿主機上的多個容器實現一定程度上的文件內容共享。
在docker容器中,宿主機被綁定的目錄我們就叫做volume(存儲卷),存儲卷帶來的好處是顯而易見的,當容器被刪除,我們也不用擔心數據丟失了,及時容器被刪除,我們也可以執行幾條命令就能把數據個容器服務再次關聯上保證服務運行。
存儲數據的周期不會受到容器的周期影響了,但是我們在創建容器的時候不可能每次都能嚴格按照一定順序,一定的命令把容器和存儲卷關聯上,萬一執行的命名少了參數,帶來的后果是極為嚴重的,所有我們還是需要容器的編排工具來完成這種事情,提前編排出來存到一個文件中,容器啟動的時候去讀取文件,減少人為出現的錯誤。
我們在把場景擴展下,假如我們宿主機使用的是共享存儲文件系統如nfs,這樣我們宿主機至上的容器在關聯目錄的時候就是關聯的nfs的之上的目錄,這樣我們的容器就不局限於在那台機器上運行了。
在實際的運用中,很大一塊其實不是在解決上層的服務搭建優化的事情上,很多時間我們都需要面臨數據的高效,可持續,動態伸縮,可遷移,負載均衡化等種種問題,及時到目前也沒有一種程序或者軟件能很好解決此類問題,就像搭建房子一樣,地基就已經決定了上層建築的高度和合理性,所以在未來的企業必然會慢慢走向雲服務化,而雲也將加速對此前短板的補齊。
存儲卷會在容器啟動的時候創建,並不需要特定的去創建,存儲卷的初衷也是獨立於容器的生命周期而存在的,因此刪除容器不會刪除卷,但是可以通過特定的命令在刪除容器的一並刪除卷。
B:docker的存儲卷的相關操作:
docker兩種類型的存儲卷:
1:bind mount volume(綁定掛載卷),這種形式的存儲卷需要在宿主機和容器上分別創建指定的路徑,然后將二者建立關聯關系
2:docekr-managed volume(docker管理卷),這種形式的存儲卷指需要在容器里面創建一個目錄,而宿主機不需要創建關聯目錄,docker會通過daemon自行創建一個空文件目錄或者一個已知的目錄來建立關聯關系,這個自行創建的目錄下文件的名稱會以容器的id號來作為文件名稱,增加了目錄的耦合關系,但是我們就不能指定路徑和目錄了,不能指定目錄的話,也就意味這容器被刪除了,再創建的時候你還是得用綁定掛載卷。不然容器又會自行生成一個存數據的文件,這樣你的數據就變兩段了
在docker中使用存儲卷:

[root@localhost ~]# docker run -it --name box1 -v /date(這里的路徑指定的是容器里面的路徑) buxybox:latest [root@localhost ~]# docker run --name box1 -it -v /dbfile busybox:latest / # ls bin dbfile dev etc home proc root sys tmp usr var / # 我們怎么知道宿主機上存儲數據的路徑了,可以docker inspect box1看下 [root@localhost ~]# docker inspect box1 ..... "Mounts": [ { "Type": "volume", "Name": "168e18da86a32c77453d4bb7c96537b0d2c31c55d75bc4a1c400b1c676a1a421", 卷的名稱 "Source": "/var/lib/docker/volumes/168e18da86a32c77453d4bb7c96537b0d2c31c55d75bc4a1c400b1c676a1a421/_data", 此路徑就是宿主機存儲數據的路徑,可以看到容器的id就是文件名 "Destination": "/dbfile", 目標路徑 "Driver": "local", "Mode": "", "RW": true, "Propagation": "" } ...... ], "ArgsEscaped": true, "Image": "busybox:latest", "Volumes": { "/dbfile": {} 在容器里面我們創建了dbfile路徑 }, ......... [root@localhost docker]# pwd /var/lib/docker [root@localhost docker]# tree volumes/ 可以看到路徑下的目錄結構 volumes/ ├── 168e18da86a32c77453d4bb7c96537b0d2c31c55d75bc4a1c400b1c676a1a421 │ └── _data └── metadata.db 我們直接在這個路徑下創建一個文件,看看容器里面能不能看到 [root@ _data]# echo "hello docker.volums" >> index.html [root@ _data]# ls index.html [root@ _data]# / # cat dbfile/index.html hello docker.volumes 可以看到我們剛才添加的內容 / # echo "dbfile" >> /dbfile/index.html 我們在容器里面加一個數據 / # cat dbfile/index.html hello docker.volumes dbfile / # [root@ _data]# cat index.html hello docker.volumes dbfile 在宿主機上也是能看到的 [root@ _data]#

[root@localhost ~]# docker run -it --name box1 -v /home/date(宿主機路徑): /date(容器路徑) busybox:latset [root@ ~]# docker run --name box2 -it --rm -v /date/volumes/box2:/htmlfile busybox:latest / # ls bin dev etc home htmlfile proc root sys tmp usr var / # cd htmlfile/ /htmlfile # ls /htmlfile # echo "hello docker.volumes" >> index.html /htmlfile # cat index.html hello docker.volumes /htmlfile # cat index.html hello docker.volumes dbfile /htmlfile # [root@ /]# cd /date/volumes/box2/ 可以看到效果是一樣的,而且這個路徑是自動創建的 [root@ box2]# ls index.html [root@ box2]# cat index.html hello docker.volumes [root@ box2]# echo "dbfile" >> index.html [root@ box2]# cat index.html hello docker.volumes dbfile [root@ box2]# 我們把容器結束掉,在看下文件是否還在 [root@ ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES [root@ ~]# cat /date/volumes/box2/index.html 可以看到文件是具備持久存儲數據的能力的 hello docker.volumes dbfile [root@ ~]# docker run --name box3 -it --rm -v /date/volumes/box2:/htmlfile busybox:latest 我們在創建一個新的容器,把文件路徑指向到之前容器路徑下去,可以看到數據還是能被新容器讀取的。 / # cat /htmlfile/index.html hello docker.volumes dbfile / # [root@ ~]# docker run --name box4 -it --rm -v /date/volumes/box2:/1/2/ busybox:latest 而且我們換容器里面的路徑還是可以繼續訪問宿主機存儲的數據 / # cat /1/2/index.html hello docker.volumes dbfile / # 我們將兩個宿主機上的兩個容器都指向一個宿主機的同一個文件 [root@localhost box2]# docker run --name box3 -it --rm -v /date/volumes/box2:/logfile busybox:latest / # cat logfile/index.html hello docker.volumes dbfile / # echo "mybusybox" >> /logfile/index.html / # [root@localhost ~]# docker run --name box1 -it --rm -v /date/volumes/box2:/dbfile busybox:latest / # cat dbfile/index.html hello docker.volumes dbfile / # cat dbfile/index.html hello docker.volumes dbfile mybusybox / #

在docker中,如果我們想查看容器的相關信息,可以使用go模板來過濾相關信息 [root@ volumes]# docker inspect -f {{.Mounts}} box4 例如我們過濾卷相關的信息,-f 然后{}這個最外層的括號是引用模板的固定格式 [{bind /date/volumes/box2 /1/2 true rprivate}] [root@ volumes]# docker inspect -f {{.NetworkSettings.Gateway}} box4 172.17.0.1 [root@ volumes]#因為使用了go語言,必然支持循環判斷等條件

啟動時候加上--volumes-from [root@localhost box2]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 90f3a89a53c7 busybox:latest "sh" About an hour ago Up About an hour box3 810de9a7256e busybox:latest "sh" About an hour ago Up About an hour box1 [root@localhost box2]# docker run --name box5 -it --rm --volumes-from box3 busybox:latest 我們在執行的時候直接指定復制box3的卷 / # ls bin dev etc home logfile proc root sys tmp usr var / # cat logfile/index.html hello docker.volumes dbfile mybusybox / # 這樣我們在規划中我們就可以提前創建一個已經最底層的持久化存儲的容器,讓其它需要使用到卷的容器取復制規划好的容器存儲卷即可,我們還可以在這個提前創建的最底層容器的時候創建共享的網絡,這樣我們可以完成更加復雜的場景。
我們能夠使用到這么多場景了,但是要去實現大量的復制類的操作或很不方便,這個時候就需要用到容器編排工具,在docker中可以使用docker-compose工具,這個工具只能編排單機,但是docker-compose也存在優勢就是能幫你創建容器,你可以把創建容器的指令都給寫到里面去。
♣六:dockerfile詳解
A:dockerfile基礎:
在docker啟動的時候我們是從dockerhub上拖下來的鏡像到本地啟動的,我們在啟動之前也沒有調整過任何的服務的配置文件,這導致我們容器啟動的時候服務默認的配置等都是dockerhub給定義好的,那如果是要修改配置文件的話,我們之前也用過docker exec命令的操作,在容器啟動之后,通過命令再去改一些文件內容,改好之后在重新加載,這種場景和我們在宿主機上的操作就一致了,當然還可以運用存儲卷的功能,把配置文件存在我們宿主機本地,讓容器啟動的時候去加載,即使這樣你也避免不了修改配置文件之后要重啟的操作,再不行我們自己做鏡像,你依然還是面臨這個問題,而且還是這三種里面最不方便的一種,如果環境多,要求的服務版本和配置還各不相同,你可能要為此做大量的不同鏡像版本。
那在docker中是怎么解決這事情的了,這個和ansible的templates類似,我們把服務的配置文件拿過來存一個模板,存到一個路徑下,然后我們把配置文件需要按照環境調整的項用環境變量的形式來指定,這樣我們在容器內部就可以使用環境變量的形式給容器里面運行的服務進行傳參了,docker畢竟不是ansible,容器在啟動的一瞬間必然需要配置文件,配置文件我們通過存儲卷的形式放在外面,docker去加載的時候變量值怎么傳給容器了,首先我們在容器主進程啟動之前先啟動一個其它的程序,這個程序就負責把用戶傳參給賦值到變量上,用戶沒有傳值的話就使用默認值,然后保存到模板配置文件中,最后我們可以使用exec把當前進程替換成所需要運行的進程即可。
通過變量的形式,我們就可以在不同的環境上去自定義我們容器啟動的配置了。
環境變量的格式和我們系統的環境變量類似:

$環境變量名=給變量賦予的指 $環境變量名=默認值(這里我們可以使用一個字符串來去指定默認值,如果變量沒有指我們就用默認值,變量有指就用變量的指) [root@localhost ~]# echo ${NAME:-hello} hello [root@localhost ~]# NAME=docker [root@localhost ~]# echo ${NAME:-hello} docker [root@localhost ~]#與之相反的就是+號,變量有指的話就用加號后面的指,沒有指就為空,這個可以用於判斷變量是否為空來使用 [root@localhost ~]# echo ${NAME:+hello} hello [root@localhost ~]# unset NAME [root@localhost ~]# echo ${NAME:+hello} [root@localhost ~]#
B:dockerfile的語法格式:
dockerfile是一個指令的集合,可以通過命令行去調用配置文件從而產生一個鏡像,dockerfile的指令也是dockerfile自己的指令,並不是我們之前使用的docker的指令,我們只需要弄清楚dockerfile的語法格式和邏輯就能使用dockerfile。
整個dockerfile語法格式就兩種:
1:注釋信息
2:由instruction開頭后面接參數組成一條指令。
instruction自身是不區分大小寫的,但是建議還是使用大寫。
dockerfile是按照指令順序來執行的,所以有指令依賴的情況就需要規划好順序了
在dockerfile中除了注釋行,整個文件的第一行應該是FROM指令,用來定義dockerfile制作的鏡像是基於那個基礎鏡像,所以說dockerfile在制作鏡像的時候必要的前提就是必須存在一個已知的鏡像在本地。
dockerfile的工作邏輯:
1:基於dockerfile來做鏡像的話必須指定一個專門的工作目錄,然后在指定的工作目錄創建或者把之前處理好的dockerfile給放到這個文件目錄里面,而且dockerfile首字母必須大寫。
2:如果dockerfile需要打包很多鏡像所使用到的文件必須是在此目錄和此目錄之下的目錄,不能是上層目錄,不然dockerfile會找不到。
假如我們基於dockerfile來做的鏡像,目錄下面有很多文件,其中有些是我們用不到或者很少很少用到的,dockerfile還支持隱藏文件,當我們遇到此類文件我們可以寫到.dockeringore里面去,當dockerfile只要讀到包含在.dockeringore文件里面記錄的路徑下文件就會自動給過濾掉,這樣可以使得我們在docker啟動的時候可以少下載一些文件,節省資源。
dockerfile制作完成之后我們可以通過docker build來讀取dockerfile文件從而來制作鏡像,最后打好標簽推導倉庫里面我們就可以使用了。
其實docker build使用的也只不過是和我們之前啟動容器之后連進去再去修改一些參數之后保存為鏡像的方式一樣的,我們之前制作容器的時候指不過需要啟動一個可見的容器,然后基於可寫層來制作,docker build也會啟動一個容器,只不過是隱藏了不可見而已。還有就是我們編輯容器可寫層時候是基於容器自帶指令去編輯的,並不是宿主機自帶的指令,比如你要在容器執行一個tree命令,你是需要自己裝包才能實現的,所有只要容器支持,我們就可以在dockerfile里面編寫shell命令,反過來說就是基於制作的鏡像不支持shell命令,你想在之后基於這個不支持shell命令的容器來寫一些shell命令是做不到的。
C:dockerfile的指令詳解:
FROM指令:from指令是dockerfile開篇非注釋的第一行,用於為鏡像文件構建過程指定鏡像文件,可以簡單理解為模板鏡像文件的指定,如果docker build在去執行的時候發現本地沒有可用的鏡像就回去你指定的鏡像站點切拉鏡像下來。比如dockerhub,如果始終找不到docker build就會報錯。
FROM 倉庫名 [鏡像名:鏡像標簽]
鏡像標簽省略的話默認使用latest標簽的鏡像
FROM 倉庫名@哈希碼(這個碼就和我們下載工具包的時候的md5一樣,防止鏡像被篡改過)
MAINTANIER:這個是用於當dockerfile制作者提供個人詳細信息,不過已經廢棄了,取代的是LABEL的指令。
MAINTANIER"名稱+聯系郵箱等"
MAINTANIER可以出現在dockerfile的任何位置,但是建議在FROM之后
LABEL:也是用於dockerfile制作者信息的創建,但是格式已經有明顯區別。
LABEL<key>=<value><key>=<value>
已經修改成鍵值對的形式,這樣可以存入跟多信息
COPY: 用於從docker主機復制文件至創建的新鏡像像文件
COPY str源文件(可以多個) dest為那個目標文件
COPY [str源文件(可以多個) dest為那個目標文件] 列表形式,這種格式主要可以避免空白字符等特殊情況,避免出現歧義
str:要復制的源文件或目錄,支持通配符,使用相對路徑
dest:目標路徑,即生成文件的目標鏡像文件所在的目錄,使用絕對路徑,否則,需要用到WORKDIR命令
COPY的復制准則:
src必須是build上下文中的路徑,不能是父目錄中的文件
如果src本身是目錄,他會遞歸的把起內部的文件或者子目錄全部復制,就類似我們cp -r的操作,但是src本身不會復制。
如指定了多個src或者在src里面使用的通配符,則dest必須是一個目錄,而且必須/結尾,不加/就是語法錯誤
如果dest事先不存在,它會被自動創建,還包括父目錄路徑

[root@localhost docker-conf-flie]# cat Dockerfile #Explain:This is a test image! FROM busybox:latest LABEL url=www.baidu.com COPY index.html /home/www/html/ 指定容器的文件在哪里取 [root@localhost docker-conf-flie]# cat index.html 我們定義文件內容 Busybox httpd.server [root@localhost docker-conf-flie]# tree . ├── Dockerfile └── index.html 只要是dockerfile用到的文件,在dockerfile平級或者在dockerfile平級目錄之下才行,除此之外,任何地方都不行的 0 directories, 2 files [root@localhost docker-conf-flie]# docker build -h Flag shorthand -h has been deprecated, please use --help Usage: docker build [OPTIONS] PATH(路徑) | URL | - Build an image from a Dockerfile Options: --add-host list Add a custom host-to-IP mapping (host:ip) --build-arg list Set build-time variables --cache-from strings Images to consider as cache sources --cgroup-parent string Optional parent cgroup for the container --compress Compress the build context using gzip --cpu-period int Limit the CPU CFS (Completely Fair Scheduler) period --cpu-quota int Limit the CPU CFS (Completely Fair Scheduler) quota -c, --cpu-shares int CPU shares (relative weight) 限制使用多少cpu --cpuset-cpus string CPUs in which to allow execution (0-3, 0,1) --cpuset-mems string MEMs in which to allow execution (0-3, 0,1) --disable-content-trust Skip image verification (default true) -f, --file string Name of the Dockerfile (Default is 'PATH/Dockerfile') --force-rm Always remove intermediate containers --iidfile string Write the image ID to the file --isolation string Container isolation technology --label list Set metadata for an image -m, --memory bytes Memory limit 限制使用多大內存 --memory-swap bytes Swap limit equal to memory plus swap: '-1' to enable unlimited swap --network string Set the networking mode for the RUN instructions during build (default "default") --no-cache Do not use cache when building the image --pull Always attempt to pull a newer version of the image -q, --quiet Suppress the build output and print image ID on success --rm Remove intermediate containers after a successful build (default true) --security-opt strings Security options --shm-size bytes Size of /dev/shm 打標簽 -t, --tag list Name and optionally a tag in the 'name:tag' format --target string Set the target build stage to build. --ulimit ulimit Ulimit options (default []) [root@localhost docker-conf-flie]# docker build -t busyboxhttpd(倉庫名):v0.1(標簽) ./(目錄,我們指定的當前目錄) Sending build context to Docker daemon 3.072kB Step 1/3 : FROM busybox:latest ---> d8233ab899d4 Step 2/3 : LABEL url=www.baidu.com 我們定義的制作鏡像的相關信息 ---> Running in 39b61095dd5d Removing intermediate container 39b61095dd5d ---> 2d5e9326ad41 Step 3/3 : COPY index.html /home/www/html/ ---> ce593c489324 Successfully built ce593c489324 Successfully tagged busyboxhttpd:v0.1 告知鏡像的名稱叫什么 [root@localhost docker-conf-flie]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE busyboxhttpd v0.1 ce593c489324 About a minute ago 1.2MB busybox latest d8233ab899d4 5 weeks ago 1.2MB [root@localhost docker-conf-flie]#可以看到我們制作好的鏡像 [root@localhost docker-conf-flie]# docker run --name boxhttpd01 --rm busyboxhttpd:v0.1 cat /home/www/html/index.html 我們可以在容器執行命令的時候接上指定的參數即可查看容器的某些內容 Busybox httpd.server 可以看到我們的dockerfile的內容已經被寫到容器里面去了 [root@localhost docker-conf-flie]#

[root@localhost /]# pwd / [root@localhost /]# tree date/ date/ └── volumes └── box2 ├── index2.html └── index.html 2 directories, 2 files [root@localhost /]# cp -r date /docker-conf-flie/ [root@localhost docker-conf-flie]# ll 總用量 8 drwxr-xr-x. 3 root root 21 3月 22 20:00 date -rw-r--r--. 1 root root 108 3月 22 19:34 Dockerfile -rw-r--r--. 1 root root 22 3月 22 19:33 index.html [root@localhost docker-conf-flie]# tree date/ date/ └── volumes └── box2 ├── index2.html └── index.html 2 directories, 2 files [root@localhost docker-conf-flie]# cat Dockerfile #Explain:This is a test image! FROM busybox:latest LABEL url=www.baidu.com COPY index.html /home/www/html/ COPY date /home/date/ 此處注意的點就是拷貝的date目錄屬於父目錄,所以說父目錄是不會被拷貝的,如果你要保證被拷貝的目錄是你將來就要使用的目錄名,那需要自己指定下目錄名,因為他只會拷貝date下的文件或目錄 [root@localhost docker-conf-flie]# docker build -t busyboxhttpd:v0.2 ./ Sending build context to Docker daemon 6.656kB Step 1/4 : FROM busybox:latest ---> d8233ab899d4 Step 2/4 : LABEL url=www.baidu.com ---> Using cache ---> 2d5e9326ad41 Step 3/4 : COPY index.html /home/www/html/ ---> Using cache ---> ce593c489324 Step 4/4 : COPY date /home/date/ ---> 0b6fa719a6bf Successfully built 0b6fa719a6bf Successfully tagged busyboxhttpd:v0.2 [root@localhost docker-conf-flie]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE busyboxhttpd v0.2 0b6fa719a6bf 8 seconds ago 1.2MB busyboxhttpd v0.1 ce593c489324 29 minutes ago 1.2MB busybox latest d8233ab899d4 5 weeks ago 1.2MB [root@localhost docker-conf-flie]# docker run --name boxhttpd2 --rm busyboxhttpd:v0.2 ls /home/date volumes [root@localhost docker-conf-flie]# docker run --name boxhttpd2 --rm busyboxhttpd:v0.2 tree /home/date docker: Error response from daemon: OCI runtime create failed: container_linux.go:344: starting container process caused "exec: \"tree\": executable file not found in $PATH": unknown. 這個就是之前我們說你的容器里面沒有安裝一些包,有些命令是沒有辦法用的,所以說你docker執行的命令是你在docker容器里面環境所帶來的命令 [root@localhost docker-conf-flie]# docker run --name boxhttpd2 --rm busyboxhttpd:v0.2 ls /home/date/volumes/box2 index.html index2.html [root@localhost docker-conf-flie]#
ADD指令:ADD指令類似COPY指令,ADD支持使用tar(壓縮包)文件和URL路徑,當遇到源文件是tar包的時候,build會自動幫忙解壓成目錄,但是這個tar必須是 本地的tar包,url網絡形式tar包是不會自動解壓的。
ADD src .. .. dest
ADD ["src" .. .. "dest"]
至於准則就和COPY一樣了

[root@localhost docker-conf-flie]# cat Dockerfile #Explain:This is a test image! FROM busybox:latest LABEL url=www.baidu.com COPY index.html /home/www/html/ COPY date /home/date/ ADD http://mirrors.shu.edu.cn/apache/tomcat/tomcat-8/v8.5.39/bin/apache-tomcat-8.5.39.tar.gz /home/tomcat/ 我們在tomcat官網下載tomcat的包放在我們的容器之中,而且/home/下是沒有tomcat這個目錄的 [root@localhost docker-conf-flie]# docker build -t busyboxhttpd:v0.3 ./ Sending build context to Docker daemon 6.656kB Step 1/5 : FROM busybox:latest ---> d8233ab899d4 Step 2/5 : LABEL url=www.baidu.com ---> Using cache ---> 2d5e9326ad41 Step 3/5 : COPY index.html /home/www/html/ ---> Using cache ---> ce593c489324 Step 4/5 : COPY date /home/date/ ---> Using cache ---> 0b6fa719a6bf Step 5/5 : ADD http://mirrors.shu.edu.cn/apache/tomcat/tomcat-8/v8.5.39/bin/apache-tomcat-8.5.39.tar.gz /home/tomcat/ Downloading [==================================================>] 9.672MB/9.672MB 可以看到這個是連到了url上下載了文件 ---> 87e5583fdad9 Successfully built 87e5583fdad9 Successfully tagged busyboxhttpd:v0.3 [root@localhost docker-conf-flie]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE busyboxhttpd v0.3 87e5583fdad9 2 minutes ago 10.9MB busyboxhttpd v0.2 0b6fa719a6bf 24 minutes ago 1.2MB busyboxhttpd v0.1 ce593c489324 About an hour ago 1.2MB busybox latest d8233ab899d4 5 weeks ago 1.2MB [root@localhost docker-conf-flie]# docker run --name boxhttpd03 --rm busyboxhttpd:v0.3 ls /home/tomcat/ apache-tomcat-8.5.39.tar.gz 可以看到並沒有幫我們解壓 [root@localhost docker-conf-flie]#

[root@localhost docker-conf-flie]# wget https://memcached.org/files/memcached-1.5.12.tar.gz 我們去找一個tar包先存在本地,看看是不是會自動幫我們解壓 --2019-03-22 20:45:01-- https://memcached.org/files/memcached-1.5.12.tar.gz 正在解析主機 memcached.org (memcached.org)... 107.170.231.145 正在連接 memcached.org (memcached.org)|107.170.231.145|:443... 已連接。 已發出 HTTP 請求,正在等待回應... 200 OK 長度:457719 (447K) [application/octet-stream] 正在保存至: “memcached-1.5.12.tar.gz” 100%[================================================================================================================>] 457,719 38.3KB/s 用時 12s 2019-03-22 20:45:14 (38.3 KB/s) - 已保存 “memcached-1.5.12.tar.gz” [457719/457719]) [root@localhost docker-conf-flie]# ls date Dockerfile index.html memcached-1.5.12.tar.gz [root@localhost docker-conf-flie]# cat Dockerfile #Explain:This is a test image! FROM busybox:latest LABEL url=www.baidu.com COPY index.html /home/www/html/ COPY date /home/date/ ADD memcached-1.5.12.tar.gz /home/memcached/ [root@localhost docker-conf-flie]# docker build -t busyboxhttpd:v0.4 ./ Sending build context to Docker daemon 464.9kB Step 1/5 : FROM busybox:latest ---> d8233ab899d4 Step 2/5 : LABEL url=www.baidu.com ---> Using cache ---> 2d5e9326ad41 Step 3/5 : COPY index.html /home/www/html/ ---> Using cache ---> ce593c489324 Step 4/5 : COPY date /home/date/ ---> Using cache ---> 0b6fa719a6bf Step 5/5 : ADD memcached-1.5.12.tar.gz /home/memcached/ ---> 1240b10a4851 Successfully built 1240b10a4851 Successfully tagged busyboxhttpd:v0.4 [root@localhost docker-conf-flie]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE busyboxhttpd v0.4 1240b10a4851 29 seconds ago 3.12MB busyboxhttpd v0.3 87e5583fdad9 13 minutes ago 10.9MB busyboxhttpd v0.2 0b6fa719a6bf 34 minutes ago 1.2MB busyboxhttpd v0.1 ce593c489324 About an hour ago 1.2MB busybox latest d8233ab899d4 5 weeks ago 1.2MB [root@localhost docker-conf-flie]# docker run --name boxhttpd04 --rm busyboxhttpd:v0.4 ls /home/memcached/ memcached-1.5.12 可以看到已經解壓 [root@localhost docker-conf-flie]# docker run --name boxhttpd04 --rm busyboxhttpd:v0.4 ls /home/memcached/memcached-1.5.12 AUTHORS COPYING ChangeLog INSTALL LICENSE.bipbuffer Makefile.am Makefile.in NEWS ......
WORKDIR:用於指定dockerfile所有的run,cmd,entrypolnt,copy和add設定工作目錄
WORKDIR: 路徑
可以指定多次,但是指定的目錄只會影響到WORKDIR只會的指令

[root@localhost docker-conf-flie]# cat Dockerfile #Explain:This is a test image! FROM busybox:latest LABEL url=www.baidu.com COPY index.html /home/www/html/ COPY date /home/date/ WORKDIR /home/memcached/ 我們指定add的工作目錄是/home/memcached/ ADD memcached-1.5.12.tar.gz ./1.5/ add之后的路徑我們就不需要指定了,因為這個當前目錄就是WORKDIR來指定了,但是我們還是可以單獨加目錄的 [root@localhost docker-conf-flie]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE busyboxhttpd v0.4 1240b10a4851 18 minutes ago 3.12MB busyboxhttpd v0.3 87e5583fdad9 31 minutes ago 10.9MB busyboxhttpd v0.2 0b6fa719a6bf About an hour ago 1.2MB busyboxhttpd v0.1 ce593c489324 About an hour ago 1.2MB busybox latest d8233ab899d4 5 weeks ago 1.2MB [root@localhost docker-conf-flie]# docker build -t busyboxhttpd:v0.5 ./ Sending build context to Docker daemon 464.9kB Step 1/6 : FROM busybox:latest ---> d8233ab899d4 Step 2/6 : LABEL url=www.baidu.com ---> Using cache ---> 2d5e9326ad41 Step 3/6 : COPY index.html /home/www/html/ ---> Using cache ---> ce593c489324 Step 4/6 : COPY date /home/date/ ---> Using cache ---> 0b6fa719a6bf Step 5/6 : WORKDIR /home/memcached/ ---> Running in 6da5b7ea24e2 Removing intermediate container 6da5b7ea24e2 ---> 0fe647ebdf19 Step 6/6 : ADD memcached-1.5.12.tar.gz ./1.5/ ---> f63491ffb75f Successfully built f63491ffb75f Successfully tagged busyboxhttpd:v0.5 [root@localhost docker-conf-flie]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE busyboxhttpd v0.5 f63491ffb75f 7 seconds ago 3.12MB busyboxhttpd v0.4 1240b10a4851 19 minutes ago 3.12MB busyboxhttpd v0.3 87e5583fdad9 32 minutes ago 10.9MB busyboxhttpd v0.2 0b6fa719a6bf About an hour ago 1.2MB busyboxhttpd v0.1 ce593c489324 About an hour ago 1.2MB busybox latest d8233ab899d4 5 weeks ago 1.2MB [root@localhost docker-conf-flie]# docker run --name boxhttpd05 --rm busyboxhttpd:v0.5 ls /home/memcached/1.5/ memcached-1.5.12 [root@localhost docker-conf-flie]#
VOLUME:在鏡像中直接定義好卷在宿主機的那個路徑,但是要知道在dockerfile下的卷是docker管理的卷,這個可以參照之前的容器卷。
VOLUME 掛載點
VOLUME ["掛載點"]

[root@localhost docker-conf-flie]# cat Dockerfile #Explain:This is a test image! FROM busybox:latest LABEL url=www.baidu.com COPY index.html /home/www/html/ COPY date /home/date/ WORKDIR /home/memcached/ ADD memcached-1.5.12.tar.gz ./1.5/ VOLUME /home/mysql/ [root@localhost docker-conf-flie]# docker build -t busyboxhttpd:v0.6 ./ Sending build context to Docker daemon 464.9kB Step 1/7 : FROM busybox:latest ---> d8233ab899d4 Step 2/7 : LABEL url=www.baidu.com ---> Using cache ---> 2d5e9326ad41 Step 3/7 : COPY index.html /home/www/html/ ---> Using cache ---> ce593c489324 Step 4/7 : COPY date /home/date/ ---> Using cache ---> 0b6fa719a6bf Step 5/7 : WORKDIR /home/memcached/ ---> Using cache ---> 0fe647ebdf19 Step 6/7 : ADD memcached-1.5.12.tar.gz ./1.5/ ---> Using cache ---> f63491ffb75f Step 7/7 : VOLUME /home/mysql/ ---> Running in 84178a58f6ba Removing intermediate container 84178a58f6ba ---> 199109e83197 Successfully built 199109e83197 Successfully tagged busyboxhttpd:v0.6 [root@localhost docker-conf-flie]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE busyboxhttpd v0.6 199109e83197 13 seconds ago 3.12MB busyboxhttpd v0.5 f63491ffb75f 9 minutes ago 3.12MB busyboxhttpd v0.4 1240b10a4851 28 minutes ago 3.12MB busyboxhttpd v0.3 87e5583fdad9 41 minutes ago 10.9MB busyboxhttpd v0.2 0b6fa719a6bf About an hour ago 1.2MB busyboxhttpd v0.1 ce593c489324 2 hours ago 1.2MB busybox latest d8233ab899d4 5 weeks ago 1.2MB [root@localhost docker-conf-flie]# docker run --name boxhttpd06 --rm busyboxhttpd:v0.6 sleep 60 讓容器執行的時候停60秒 [root@localhost ~]# docker inspect -f {{.Mounts}} boxhttpd06 我們來看下容器卷的詳細信息 [{volume 5ba8e23864423b049d6cac54d6dd19d74808b7526bb0aaa0c74ce4b372625e30 /var/lib/docker/volumes/5ba8e23864423b049d6cac54d6dd19d74808b7526bb0aaa0c74ce4b372625e30/_data /home/mysql(掛載點) local true }] [root@localhost ~]#
EXPOSE:為容器打開容器指定要監聽的端口保證和外部通訊,自動幫我們生成了nat規則,但是不能幫我們去指定那個ip去綁定,屬於動態綁定,意味着這個綁定就是宿主機的所有端口和隨機端口。
這里的隨機端口和所有地址是因為dockerfile未來是你不確定要放在那台機器上面,還有未來容器的端口不一定是要暴露在外面的。
EXPOSE port(端口 ) [protocol]
protocol(指傳輸層協議,可以是tcp或者udp,默認是tcp
EXPOSE指令可以一次指定多個端口,EXPOSE 123/udp 234/tcp

[root@localhost docker-conf-flie]# cat Dockerfile #Explain:This is a test image! FROM busybox:latest LABEL url=www.baidu.com COPY index.html /home/www/html/ COPY date /home/date/ WORKDIR /home/memcached/ ADD memcached-1.5.12.tar.gz ./1.5/ VOLUME /home/mysql/ EXPOSE 8088/tcp 9099/udp [root@localhost docker-conf-flie]# docker build -t busyboxhttpd:v0.7 ./ Sending build context to Docker daemon 464.9kB Step 1/8 : FROM busybox:latest ---> d8233ab899d4 Step 2/8 : LABEL url=www.baidu.com ---> Using cache ---> 2d5e9326ad41 Step 3/8 : COPY index.html /home/www/html/ ---> Using cache ---> ce593c489324 Step 4/8 : COPY date /home/date/ ---> Using cache ---> 0b6fa719a6bf Step 5/8 : WORKDIR /home/memcached/ ---> Using cache ---> 0fe647ebdf19 Step 6/8 : ADD memcached-1.5.12.tar.gz ./1.5/ ---> Using cache ---> f63491ffb75f Step 7/8 : VOLUME /home/mysql/ ---> Using cache ---> 199109e83197 Step 8/8 : EXPOSE 8088/tcp 9099/udp ---> Using cache ---> 339083b60682 Successfully built 339083b60682 Successfully tagged busyboxhttpd:v0.7 [root@localhost docker-conf-flie]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE busyboxhttpd v0.6 339083b60682 26 seconds ago 3.12MB busyboxhttpd v0.7 339083b60682 26 seconds ago 3.12MB busyboxhttpd v0.5 f63491ffb75f 12 hours ago 3.12MB busyboxhttpd v0.4 1240b10a4851 13 hours ago 3.12MB busyboxhttpd v0.3 87e5583fdad9 13 hours ago 10.9MB busyboxhttpd v0.2 0b6fa719a6bf 13 hours ago 1.2MB busyboxhttpd v0.1 ce593c489324 14 hours ago 1.2MB busybox latest d8233ab899d4 5 weeks ago 1.2MB [root@localhost docker-conf-flie]# docker run --name boxhttpd07 --rm busyboxhttpd:v0.7 /bin/httpd -f -h /home/www/html 運行之后我們查看服務訪問情況 [root@localhost ~]# docker inspect -f {{.NetworkSettings.IPAddress}} boxhttpd07 172.17.0.2 [root@localhost ~]# curl 172.17.0.2 Busybox httpd.server [root@localhost ~]# docker port boxhttpd07 我們可以發現此時服務的端口並不會暴露對外監聽 [root@localhost docker-conf-flie]# docker run --name boxhttpd07 --rm -P busyboxhttpd:v0.7 /bin/httpd -f -h /home/www/html 我們再次運行加上P參數,這個參數之前提到過,我們不需要在去P參數后面指定端口了,dockerfile已經指定了。 [root@localhost ~]# docker port boxhttpd07 8088/tcp -> 0.0.0.0:32768 9099/udp -> 0.0.0.0:32768 [root@localhost ~]#可以看到端口暴露對外監聽了
ENV:用於為鏡像定義所需要的環境變量,可被ENV,ADD,COPY等命令所調用
ENV key vaiue 這種格式下key之后所有內容都會被當做value的組成部分,因此一次只能給一個變量
ENV key=value\key=value 這種就解決了上面一次只能給一個變量的問題,通過反斜線(續行)間隔,通過一個ENV給到多個值,建議是用第二種方式
反斜線還能對空格進行轉義
寫法1 FROM busybox:latest LABEL url=www.baidu.com ENV WEBFILE /home/www/html/ 如果就一個值前面是變量名后面就是指 COPY index.html $WEBFILE 在需要引用變量的地方直接$符號接上變量即可 ..... 寫法2: FROM busybox:latest LABEL url=www.baidu.com ENV WEBFILE /home/www/html/ 如果就一個值前面是變量名后面就是指 COPY index.html ${WEBFILE:-/home/www/html/} 如果變量沒有值就用-號后面我們定義的值,此處參考文章中變量賦值的內容 ..... 寫法3: #Explain:This is a test image! FROM busybox:latest LABEL url=www.baidu.com ENV WEBFILE=/home/www/html/\ 用反斜線續行寫 MEM_EDITION="memcached-1.5.12.tar.gz" COPY index.html ${WEBFILE:-/home/www/html/} COPY date /home/date/ WORKDIR /home/memcached/ ADD ${MEM_EDITION} ./1.6/ ....

[root@localhost docker-conf-flie]# docker build -t busyboxhttpd:v0.8 ./ Sending build context to Docker daemon 464.9kB Step 1/9 : FROM busybox:latest ---> d8233ab899d4 Step 2/9 : LABEL url=www.baidu.com ---> Using cache ---> 2d5e9326ad41 Step 3/9 : ENV WEBFILE=/home/www/html/ MEM_EDITION="memcached-1.5.12.tar.gz" ---> Running in 8717be855cb6 Removing intermediate container 8717be855cb6 ---> d2728a778315 Step 4/9 : COPY index.html ${WEBFILE:-/home/www/html/} ---> 32f9c8bfe225 Step 5/9 : COPY date /home/date/ ---> d154c2fc16a1 Step 6/9 : WORKDIR /home/memcached/ ---> Running in 6059bbf44d67 Removing intermediate container 6059bbf44d67 ---> bba487a7779e Step 7/9 : ADD ${MEM_EDITION} ./1.6/ ---> 2a22046822b8 Step 8/9 : VOLUME /home/mysql/ ---> Running in 42ebc490d313 Removing intermediate container 42ebc490d313 ---> c3d946d88690 Step 9/9 : EXPOSE 8088/tcp 9099/udp ---> Running in 1751ba881e93 Removing intermediate container 1751ba881e93 ---> 34af5bf9ae82 Successfully built 34af5bf9ae82 Successfully tagged busyboxhttpd:v0.8 [root@localhost docker-conf-flie]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE busyboxhttpd v0.8 34af5bf9ae82 16 minutes ago 3.12MB busyboxhttpd v0.6 339083b60682 2 hours ago 3.12MB busyboxhttpd v0.7 339083b60682 2 hours ago 3.12MB busyboxhttpd v0.5 f63491ffb75f 14 hours ago 3.12MB busyboxhttpd v0.4 1240b10a4851 15 hours ago 3.12MB busyboxhttpd v0.3 87e5583fdad9 15 hours ago 10.9MB busyboxhttpd v0.2 0b6fa719a6bf 15 hours ago 1.2MB busyboxhttpd v0.1 ce593c489324 16 hours ago 1.2MB busybox latest d8233ab899d4 5 weeks ago 1.2MB [root@localhost docker-conf-flie]# docker run --name boxhttpd08 --rm busyboxhttpd:v0.8 ls /home/memcached/1.6/ memcached-1.5.12 [root@localhost docker-conf-flie]# docker run --name boxhttpd08 --rm busyboxhttpd:v0.8 ls /home/www/html/ index.html [root@localhost docker-conf-flie]#如果變量傳值dockerfile里面我們還不如直接指定,ENV加了之后我們反而還要多寫幾行。 其實ENV的作用就在於我們在啟動容器的時候給變量傳參,而不是在dockerfile里面。 [root@localhost docker-conf-flie]# docker run --name boxhttpd08 --rm busyboxhttpd:v0.8 printenv 我們查看在容器里面的變量 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=a020c6b82d59 WEBFILE=/home/www/html/ MEM_EDITION=memcached-1.5.12.tar.gz 可以看到這些變量已經存有了值 HOME=/root [root@localhost docker-conf-flie]# docker run --name boxhttpd08 -e MEM_EDITION="memcached-1.6.6.tar.gz" --rm busyboxhttpd:v0.8 printenv 我們通過-e參數可以在鏡像初始化為容器的時候給環境變量賦值。 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=4568f836ef4b MEM_EDITION=memcached-1.6.6.tar.gz WEBFILE=/home/www/html/ HOME=/root [root@localhost docker-conf-flie]# docker run --name boxhttpd08 --rm busyboxhttpd:v0.8 ls /home/memcached/1.6/ memcached-1.5.12 可以看到文件並沒有變化,這個是因為我們在build的時候是基於容器做鏡像,解壓的這一步其實在build的時候就已經完成了,我們在做成的鏡像上run的時候已經是新的鏡像了,解壓的文件已經在build的是形成了,run的時候再給變量賦值只不過是第二階段的事情了,不會影響build。 env在dockerfile適合用於出現反復的目錄和文件等。
RUN:在dockerfile添加命令,讓其在build的時候去執行命令,從而制作成鏡像,這個RUN和CMD是相似的,不過執行的階段不一樣,RUN是build階段,CMD是run啟動容器的時候。
RUN command
這種格式中,command通常是一個shell命令,也就意味着先啟動的是shell進程,會以“/bin/sh -c“來運行的,這意味着此進程在容器的id不是1,不能接受uninx信號,當使用 docker stop想停止你容器里面的程序的進程是接收不到的。
RUN ["executanle","param1",....]
第二種語法參數是json的數組,executanle是一個可執行程序的路徑下的命令,param1到param...為傳遞給命令的選項和參數,此種格式下不會以/bin/sh -c來運行,直接由內核啟 動,所以這種情況是不支持shell下的通配符等操作,例如(>,*,|)這樣的符號。
RUN ["/bin/bash","-c","executanle","param1",....]
如果想解決第二種不能用shell的情況就可以使用第三種格式

[root@localhost docker-conf-flie]# cat Dockerfile #Explain:This is a test image! FROM busybox:latest LABEL url=www.baidu.com ENV TEM_EDITION="http://mirrors.shu.edu.cn/apache/tomcat/tomcat-8/v8.5.39/bin/apache-tomcat-8.5.39.tar.gz" 在dockerfile中只要是url上下載的tar包docker是不能解壓的 ADD ${TEM_EDITION} /home/tomcat/ RUN cd /home/tomcat&&\ tar xf *.tar.gz 我們需要手動解壓,這個命令要注意的是dockerfile可能對我們在命令行習慣輸入的-xf前面的橫崗不識別的問題 [root@localhost docker-conf-flie]# docker run --name boxhttpd09 --rm busyboxhttpd:v0.9 ls /home/tomcat/ apache-tomcat-8.5.39 可以看到文件已經正常解壓 apache-tomcat-8.5.39.tar.gz run是可以執行多條命令的,如果有多條命令都是處理一個東西的,要保證在一行run里面執行完畢。 只要有的run你指需要考慮你的基礎鏡像能不能支持你需要操作的命令,只要支持,那你完全可以按照你的要求吧鏡像調整為你想實現的樣子。 run在制作鏡像的時候執行的命令屬於bin/sh命令,當你鏡像制作完成了,是運行的nginx,那就需要把默認運行的bin/sh改成nginx了,而要實現這個是要通過CMD來完成的。
CMD:在docker run容器運行啟動的時候可以通過cmd來執行一些命令,而RUN命令是build的時候已經執行完成了,所以你想在容器運行的時候執行命令就需要
使用到CMD命令。
為什么我們要在dockerfile中給到CMD命令,因為我們的一個容器只負責跑一個程序也就是和這個程序相關的進程存在即可,無需跑其它的進程來浪費容器的資源,我們的linux啟動 之后默認就運行了sh相關的進程,我們才可以使用sh相關的命令,但是在容器里面我們也許不需要sh相關的進程存在,我們只需要一個提供服務的nginx,redis相關的進程生效即 可,這個時候我們就需要剝離原有的sh,讓nginx啟動的時候就為默認的進程。可以把容器就當做一個正常服務,正常服務運行的時候是不需要其他的進程來參與其中的。
CDM在dockerfile中默認只能生效一個,也就是dockerfile中最后一個CMD命令,所以說CMD即使給了多次也只能生效最后一個。
CMD運行在鏡像啟動為容器的時候
CMD command
這種情況和RUN的一樣,默認shell先啟動運行,但是想docker stop停止服務進程是做不到的。
CMD ["executanle","param1",....]
和RUN一樣
CMD ["param1","param2",....]
第三種和RUN需要結合ENTRYPOINT指令提供默認參數來運行。

[root@ docker-conf-flie]# cat Dockerfile #Explain:This is a test image! FROM busybox:latest LABEL url=www.baidu.com ENV WEB_HTML_FILE="/www/html/" RUN mkdir -p $WEB_HTML_FILE &&\ echo "hello bosybox.html" > ${WEB_HTML_FILE}/index.html 創建目錄的命令是屬於/bin/sh的,這意味着還是需要調用/bin/sh來完成創建 CMD /bin/httpd -f -h ${WEB_HTML_FILE} 當我們執行build之后,在啟動鏡像為容器的時候直接執行httpd的命令,從而占據進程標號為1的位置,當有其他的命令的時候為httpd的子進程。 還需要注意的是${WEB_HTML_FILE}這個shell的環境變量,還意味着httpd啟動還是需要/bin/sh來完成,那么httpd還是在只要涵蓋/bin/sh的時候的還是/bin/sh的子進程,不過啟動之后通過cmd可以給替換掉 [root@ docker-conf-flie]# docker run --name boxhttpd1.0 -it --rm busyboxhttpd:v1.0 我們發現啟動容器之后不會再出現/#的符號,因為現在是處於httpd的進程,不再是/bin/sh [root@ ~]# docker image inspect busyboxhttpd:v1.0 ...... ], "Cmd": [ "/bin/sh", "-c", 可以看到cmd命令確實以/bin/sh -c來啟動了httpd "/bin/httpd -f -h ${WEB_HTML_FILE}" ], [root@ ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 136e25bcd07c busyboxhttpd:v1.0 "/bin/sh -c '/bin/ht…"可以看到確實是以/bin/sh -c來啟動的 15 minutes ago Up 15 minutes boxhttpd1.0 1af850cc1a46 89c27a4b0cd8 "/bin/sh -c 'mkdir $…" 22 minutes ago Exited (1) 22 minutes ago unruffled_dewdney 98ab876bf458 89c27a4b0cd8 "/bin/sh -c 'mkdir $…" 22 minutes ago Exited (1) 22 minutes ago elastic_robinson 881b15b72bbf busyboxhttpd:v0.9 "sh" 2 days ago Exited (0) 2 days ago boxhttpd09 [root@ ~]# docker exec -it boxhttpd1.0 /bin/sh 我們使用exec進去的時候以/bin/sh形式來看下 / # ps PID USER TIME COMMAND 1 root 0:00 /bin/httpd -f -h /www/html/ 可以發現進行id為1的就是httpd的進程,這里保留的id是方法是方便容器能夠接受unix信號,我們能通過docker kill或者stop能停止容器,這樣我們通過cmd就吧程序默認啟動運行時的環境給改了。 16 root 0:00 /bin/sh 我們exec調用/bin/sh已經淪為httpd的子進程了 22 root 0:00 ps / # cat www/html/index.html hello bosybox.html / #

[root@ docker-conf-flie]# cat Dockerfile #Explain:This is a test image! FROM busybox:latest LABEL url=www.baidu.com ENV WEB_HTML_FILE="/www/html/" RUN mkdir -p $WEB_HTML_FILE &&\ echo "hello bosybox.html" > ${WEB_HTML_FILE}/index.html #CMD /bin/httpd -f -h ${WEB_HTML_FILE} CMD ["/bin/httpd","-f","-h ${WEB_HTML_FILE}"] 我們將cmd改成json數組的形式 [root@ docker-conf-flie]# docker image inspect busyboxhttpd:v1.1 ..... "Cmd": [ "/bin/httpd", "-f", "-h ${WEB_HTML_FILE}" ], 我們可以看到cmd已然不是之前/bin/sh了 [root@ docker-conf-flie]# docker run --name boxhttpd1.1 -it busyboxhttpd:v1.1 WARNING: IPv4 forwarding is disabled. Networking will not work. httpd: can't change directory to ' ${WEB_HTML_FILE}': No such file or directory 我們在運行容器的時候就有報錯,這個是因為json格式默認不會以shell的子進程來運行,當出現-h ${WEB_HTML_FILE}會解析成為路徑,也就會報這個路徑不存在 [root@ docker-conf-flie]# cat Dockerfile #Explain:This is a test image! FROM busybox:latest LABEL url=www.baidu.com ENV WEB_HTML_FILE="/www/html/" RUN mkdir -p $WEB_HTML_FILE &&\ echo "hello bosybox.html" > ${WEB_HTML_FILE}/index.html #CMD /bin/httpd -f -h ${WEB_HTML_FILE} CMD ["/bin/sh","-c","/bin/httpd","-f","-h ${WEB_HTML_FILE}"] 我們手動添加指定運行的環境由/bin/sh來解析 [root@ docker-conf-flie]# docker run --name boxhttpd1.2 -it busyboxhttpd:v1.2 WARNING: IPv4 forwarding is disabled. Networking will not work. 在運行的時候容器直接退出了 [root@ docker-conf-flie]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 304417fbe2ef busyboxhttpd:v1.2 "/bin/sh -c /bin/htt…" 4 seconds ago Exited (0) 3 seconds ago boxhttpd1.2 可以看到容器處於退出的狀態,至少證明我們的dockerfile寫法是沒有錯誤的,這種情況下我們就需要借助docke的logs輸出來看下到底是什么問題,最大的原因應該和我們的httpd路徑的調用方式有關。 大多數情況下我們的應用程序啟動如果沒有問題,/bin/sh -c也應該是不會有問題的
CMD的第三種命令需要結合ENTRYPOINT來完成
ENTRYPOINT:類似cmd的功能,用於為容器指定默認運行的程序,使得容器像是一個單獨的和執行程序。

[root@ docker-conf-flie]# docker run --name boxhttpd1.2 -it busyboxhttpd:v1.2 ls /www/html/ WARNING: IPv4 forwarding is disabled. Networking will not work. index.html [root@ docker-conf-flie]# 當我們在運行容器的時候可以讓容器不運行本身在dockerfile定義好的內容,轉而使用我們后面定義的命令來覆蓋dockerfile制作好鏡像的命令,那這樣就有訴求我不想容器運行的時候默認的命令被覆蓋,cmd是做不到的,需要使用ENTRYPOINT命令,這也是ENTRYPOINT與cmd命令的不同之處,ENTRYPOINT啟動的程序不會被docker run命令指定的參數所覆蓋,而且這些命令參數會被當做參數傳遞給ENTRYPOINT指定的程序。 ENTRYPOINT command ENTRYPOINT ["executable","para1".....] [root@localhost docker-conf-flie]# cat Dockerfile #Explain:This is a test image! FROM busybox:latest LABEL url=www.baidu.com ENV WEB_HTML_FILE="/www/html/" RUN mkdir -p $WEB_HTML_FILE &&\ echo "hello bosybox.html" > ${WEB_HTML_FILE}/index.html #CMD /bin/httpd -f -h ${WEB_HTML_FILE} #CMD ["/bin/sh","-c","/bin/httpd","-f","-h ${WEB_HTML_FILE}"] ENTRYPOINT /bin/httpd -f -h ${WEB_HTML_FILE} [root@ docker-conf-flie]# docker run --name boxhttpd1.3 -it --rm -P busyboxhttpd:v1.3 ls /www/html/ WARNING: IPv4 forwarding is disabled. Networking will not work. 我們把cmd替換成ENTRYPOINT,再去執行ls的時候發現並沒有出現上面的情況,這個是因為程序默認啟動的是httpd,也就是/bin/httpd -f -h ${WEB_HTML_FILE}這一長傳的命令,然后把ls這個命令當做參數附在httpd后面,只不過httpd識別不了這個參數不會運行罷了。 然而ENTRYPOINT也是可以被覆蓋的,可以通過特定的選項--entrypoint就能完成,docker留有這個參數是讓用戶明確自己的操作 [root@ docker-conf-flie]# docker run --name boxhttpd1.4 -it --rm -P --entrypoint "top" busyboxhttpd:v1.4 可以發現--entrypoint我們指定的top命令就覆蓋了原先的定義好的命令了 WARNING: IPv4 forwarding is disabled. Networking will not work. Mem: 1340904K used, 522348K free, 12752K shrd, 2116K buff, 584808K cached CPU: 0.0% usr 5.0% sys 0.0% nic 95.0% idle 0.0% io 0.0% irq 0.0% sirq Load average: 0.00 0.01 0.05 2/408 5 PID PPID USER STAT VSZ %VSZ CPU %CPU COMMAND 1 0 root R 1280 0.0 2 0.0 top [root@ docker-conf-flie]# cmd和ENTRYPOINT在dockerfile中能定義多個,但是只有最后一個生效。
CMD和ENTRYPOINT混合使用:
當CMD和ENTRYPOINT混合使用的時候,cmd默認則用於為ENTRYPOINT提供默認參數。
CMD ["para1","para2",.....]
此時cmd只能是數組的格式

[root@localhost docker-conf-flie]# cat Dockerfile #Explain:This is a test image! FROM busybox:latest LABEL url=www.baidu.com ENV WEB_HTML_FILE="/www/html/" RUN mkdir -p $WEB_HTML_FILE &&\ echo "hello bosybox.html" > ${WEB_HTML_FILE}/index.html #CMD /bin/httpd -f -h ${WEB_HTML_FILE} CMD ["/bin/httpd","-f","-h ${WEB_HTML_FILE}"] ENTRYPOINT ["/bin/sh","-c"] 此處ENTRYPOINT只能使用數組的形式,因為ENTRYPOINT運行的時候可能會用到/bin/sh -c,如果是沒有以數組的形式的話,那就是以/bin/sh -c運行/bin/sh -c了 [root@localhost docker-conf-flie]# docker run --name boxhttpd1.4 -P busyboxhttpd:v1.5因為內部變量交叉混合應用容器是不會起來的 [root@localhost docker-conf-flie]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b9d6790bbc5f busyboxhttpd:v1.5 "/bin/sh -c /bin/htt…" 3 seconds ago Exited (0) 2 seconds ago boxhttpd1.4 但是我們能看到程序是/bin/sh -c /bin/httpd來運行的 [root@localhost docker-conf-flie]# docker inspect -f{{.Config.Cmd}} boxhttpd1.4 [/bin/httpd -f -h ${WEB_HTML_FILE}] [root@localhost docker-conf-flie]# docker inspect -f{{.Config.Entrypoint}} boxhttpd1.4 [/bin/sh -c] [root@localhost docker-conf-flie]# docker run --name boxhttpd1.4 -P busyboxhttpd:v1.5 "ls /www/html" 傳參的時候加雙引號避免出現錯誤 index.html 當cmd和Entrypoint同事出現的時候,cmd代表默認參數傳輸了Entrypoint了,當Entrypoint自己有參數(我們在啟動時給的ls參數)就會覆蓋cmd的參數,而程序此時運行的ENTRYPOINT ["/bin/sh","-c"]的進程,ls也就能正常運行了。 通過上面的案例,我們發現CMD和ENTRYPOINT混合使用的時候按照我們運行的情況來看ENTRYPOINT是完全沒有必要使用的,本來可以在cmd里面一行寫掉的我們反而多此一舉了。

[root@localhost docker-conf-flie]# cat Dockerfile #Explain:This is a test image! FROM nginx:1.14-alpine LABEL url=www.nginx.com ENV WEB_HTML_FILE="/www/html/" 網頁存在的路徑 ADD entrypoint.sh /bin/ 復制主機我們已經寫好的entrypoint.sh腳本到鏡像里面,給到后面的ENTRYPOINT去運行。 ADD index.html ${WEB_HTML_FILE} 復制主機的我們寫好的網頁文件到鏡像里面去。 CMD ["/bin/nginx","-g","daemon off;"] 我們要運行的命令 -g標示設定全局選項。 daemon off這個是為了讓nginx運行在前台,daemon是守護進程,off是不要運行為守護進程,nginx的配置文件都是分號結尾的,cmd里面也是一樣。 ENTRYPOINT ["/bin/entrypoint.sh"] 運行add進來的entrypoint.sh腳本,但是我們上面的案例也看到了,一旦ENTRYPOINT有了默認參數,就會覆蓋cmd要運行的命令,這個時候就需要改 我們的entrypoint.sh腳本。 [root@localhost docker-conf-flie]# cat entrypoint.sh #!/bin/sh #alpine的鏡像是沒有bash的只有sh cat > /etc/nginx/conf.d/www.conf << EOF server { server_name ${HOSTNAME}; listen ${IP:-0.0.0.0}:${PORT:-80}; :-代表默認參數 root ${WEB_HTML_FILEI:-/usr/share/nginx/html}; } EOF exec "$@" $@代表當前腳本的所有參數,參數是什么我就運行什么,而且運行完成之后我還要exec退出當前進程。 1:我們先寫一個用於初始化定義nginx運行環境的腳本,啟動定義的ip和端口,網頁的路徑等信息。 2:通過add把這個腳本拷貝進鏡像里面,啟動為容器之后,然后ENTRYPOINT在容器里面運行entrypoint.sh腳本,環境初始化完成再去執行cmd的命令,而且cmd執行成功之后就會頂替sh進程從而成為容器里面唯一的進程。 [root@localhost docker-conf-flie]# docker run --name myweb1.1 --rm -it -P mynginxweb:v1.1啟動容器 [root@localhost docker-conf-flie]# docker exec -it myweb1.1 /bin/sh 通過從容器外面指令命令去看容器內的nginx情況 / # cat /www/html/index.html Busybox nginx.server / # netstat -anptu Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1/nginx -g daemon o / # ps 可以看到編號為1的進程是我們的nginx PID USER TIME COMMAND 1 root 0:00 nginx: master process /usr/sbin/nginx -g daemon off; 8 nginx 0:00 nginx: worker process 32 root 0:00 /bin/sh 37 root 0:00 ps / # cat /etc/nginx/conf.d/www.conf 配置文件也是我們從外面定義好的文件 server { server_name 928e9855e425; 主機名是我們的容器id,我們也可以通過特定的指令來完成 listen 0.0.0.0:80; root /usr/share/nginx/html; } / # [root@localhost ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 928e9855e425 mynginxweb:v1.1 "/bin/entrypoint.sh …" 28 minutes ago Up 28 minutes 0.0.0.0:32778->80/tcp myweb1.1 [root@localhost ~]# 我們將容器的運行環境用變量定義的好處就是我們能在外面給容器傳參。 [root@localhost docker-conf-flie]# docker run --name myweb1.1 --rm -it -P -e "PORT=8088" mynginxweb:v1.1 運行容器的時候用-e選項給容器的變量傳參,當然這些環境變量名必須是在容器里面能查到的。 / # [root@localhost docker-conf-flie]# docker exec -it myweb1.1 /bin/sh / # netstat -anptu Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:8088 0.0.0.0:* LISTEN 1/nginx -g daemon o tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1/nginx -g daemon o / # [root@localhost docker-conf-flie]# docker run --name myweb1.1 --rm -it -P -e "HOSTNAME=myngixnweb1.1" mynginxweb:v1.1 [root@localhost docker-conf-flie]# docker exec -it myweb1.1 /bin/sh / # cat /etc/nginx/conf.d/www.conf server { server_name myngixnweb1.1; listen 0.0.0.0:80; root /usr/share/nginx/html; } / #
在dockerhub上很多服務鏡像都是使用ENTRYPOINT執行腳本接受參數之后並啟動服務的,如果想寫一個復雜的Dockerfile可以參考dockerhub上的案例
注:Dockerfile里面只要用到數組類型的都要使用雙引號才行,否則會有錯誤。
USER:用於指定運行鏡像或運行Dockerflie中任何的run,cmd,ENTRYPOINT指令指令的程序的用戶名和UID
默認情況用戶的身份為root
USER uid 用戶名
需要注意的是uid可以是任意數字,但是這個數字必須存在容器中/etc/passwd中所存在某個有效用戶的uid,否則docker run將失敗。
[root@localhost docker-conf-flie]# docker exec -it myweb1.1 /bin/sh / # ps PID USER TIME COMMAND 1 root(進程運行的用戶是root) 0:00 nginx: master process /usr/sbin/nginx -g daemon off; 8 nginx 0:00 nginx: worker process 32 root 0:00 /bin/sh 37 root 0:00 ps
HEALTHCHECK:用於CMD之后接一個固定命令用來檢查我們的主進程狀態是否正常和健康。如果不想健康狀態檢查可以接NONE來拒絕任何狀態檢查,也包括默認的檢查機制。除此之外CMD前面還可以接[OPTION]用於周期性的
--intterval=DURATION(default:30s) 默認周期檢查是30s,可以設定為其它時間
--timeout=DURATION(default:30s) 當我們周期性檢查發起請求了,超過了多少時間就認為是有問題的。
--start-period=DURATION(default:0s) 當我們容器啟動起來了,容器里面的進程是需要初始化等一系列工作的,這個必然就會導致有個進程初始化時間,但是我們的健康檢查是 隨着容器一啟動就開始了,很可能此時容器里面的進程還在初始化,這個時候健康檢查就會認為進程狀態異常就直接給kill掉了,那這種情況容器就永遠起不來了,所以--start- period就是用來設定一個時間,等主進程初始化完成之后在進行健康檢查,如果進程啟動需要2秒,--start-period就可以設置為5秒之后在檢查。
--retries=數字(default:3) 判斷一次超過多長時間就判定不正常是不嚴謹的,--retries就是用來定義判斷次數的,可以多設置幾次。
當上面的檢查發起了,會返回一些狀態:
0:success 正常的
1:unhealthy 不正常的
2:reserved 預留的,可以自己定義
例如HEALTHCHECK --intterval=5m --timeout=3s CMD curl -f http://localhost/ || exit 1(這個1就代表1:unhealthy 不正常的)

[root@localhost docker-conf-flie]# cat Dockerfile #Explain:This is a test image! FROM nginx:1.14-alpine LABEL url=www.nginx.com ENV WEB_HTML_FILE='/www/html/' ADD entrypoint.sh /bin/ ADD index.html ${WEB_HTML_FILE} HEALTHCHECK --start-period=5s --interval=6s CMD wget -O - -q http://${IP:-0.0.0.0}:8088/ CMD ["/usr/sbin/nginx","-g","daemon off;"] ENTRYPOINT ["/bin/entrypoint.sh"] [root@localhost docker-conf-flie]# docker run --name myweb1.2 --rm -P -e "PORT=8088" mynginxweb:v1.2 127.0.0.1 - - [30/Mar/2019:06:55:23 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-" 可以看到檢查是成功的,而且是每隔6秒檢查一次 127.0.0.1 - - [30/Mar/2019:06:55:29 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-" 127.0.0.1 - - [30/Mar/2019:06:55:35 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-" 127.0.0.1 - - [30/Mar/2019:06:55:41 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-" [root@localhost docker-conf-flie]#
SHELL:用來定義程序運行環境的默認shell環境,如果是你的docker是在win系統上運行的話那就需要去指定運行的環境,上面我們一直是/bin/sh -c的環境,如果你的基礎鏡像是/bin/bash這個時候就需要指定了。
STOPSIGNAL:之前我們說容器里面的進程執行之后可以接收stop指令來停止,是因為docker想容器內部發了一個信號-15,這個-15就代表了停止,如果是想換其它的信號。
STOPSIGNAL 信號名,比如信號9,就等於直接kill掉了。
AGE:和ENV很相似,只要也是支持變量,但是這個變量是在build的時候去使用的,這個好處就在於我們的Dockerfile能適應更多不同的環境來制作鏡像文件

[root@localhost docker-conf-flie]# cat Dockerfile #Explain:This is a test image! FROM nginx:1.14-alpine ARG url="www.123.com" LABEL maintainer="${url}" ENV WEB_HTML_FILE='/www/html/' ADD entrypoint.sh /bin/ ADD index.html ${WEB_HTML_FILE} HEALTHCHECK --start-period=5s --interval=6s CMD wget -O - -q http://${IP:-0.0.0.0}:8088/ CMD ["/usr/sbin/nginx","-g","daemon off;"] ENTRYPOINT ["/bin/entrypoint.sh"] [root@localhost docker-conf-flie]# docker image inspect -f {{.ContainerConfig.Labels}} mynginxweb:v1.4 map[maintainer:www.321.com] 可以看到我們就可以在build命令行的同時給傳參了 [root@localhost docker-conf-flie]#
ONBUILD:用於在Dockerfile中定義一個觸發器,這個觸發器不是在build的時候執行的,是在你build制作成鏡像之后,別人在拿你的鏡像作為基礎鏡像來做新的鏡像的時候觸發執行,這種執行又叫延時執行。
ONBUILD dockerfile的指令
ONBUILD不能自我嵌套,不能觸發FROM和MAINTAINER指令
使用包含ONBUILD指令的dockerfile構建的鏡像應該使用特殊的標簽
ONBUILD指令中使用ADD和COPY的時候應該要注意上下文中定義的源文件是否缺失,缺失就會失敗。

[root@localhost docker-conf-flie]# cat Dockerfile #Explain:This is a test image! FROM nginx:1.14-alpine ARG url="www.123.com" LABEL maintainer="${url}" ENV WEB_HTML_FILE='/www/html/' ADD entrypoint.sh /bin/ ADD index.html ${WEB_HTML_FILE} HEALTHCHECK --start-period=5s --interval=6s CMD wget -O - -q http://${IP:-0.0.0.0}:8088/ CMD ["/usr/sbin/nginx","-g","daemon off;"] ONBUILD ADD https://sourceforge.net/projects/htop/files/latest/download /www/html/ 定義一個下載的內容 ENTRYPOINT ["/bin/entrypoint.sh"] [root@localhost docker-conf-flie]# docker run --name myweb1.5 --rm mynginxweb:v1.5 ls /www/html/ index.html [root@localhost docker-conf-flie]#可以看到我們制作的鏡像啟動為容器之后並不會去下載這個文件。 [root@localhost test]# pwd /home/test [root@localhost test]# cat Dockerfile 創建另外一個dockerfile FROM mynginxweb:v1.5 基礎鏡像用我們上面定義的一個 RUN mkdir /test/ [root@localhost test]# docker build -t test:v1.1 ./ Sending build context to Docker daemon 2.048kB Step 1/2 : FROM mynginxweb:v1.5 # Executing 1 build trigger Downloading [==================================================>] 388.5kB/388.5kB 我們在build的時候就發現有一個下載的內容 ---> f8e5d36e5950 Step 2/2 : RUN mkdir /test/ ---> Running in 1634f5932da8 Removing intermediate container 1634f5932da8 ---> 22eefe8da83d Successfully built 22eefe8da83d Successfully tagged test:v1.1 [root@localhost test]# docker run --name test1.1 --rm test:v1.1 ls /www/html/ download index.html 而且文件也被下載下來了。
如果dockerfile的里面存在源文件,如果是你的鏡像源文件如果是ftp等公司內部的站點可以下載到,那就很方便了,直接add下來新的鏡像就完成了,當然這種場景適合於私有鏡像。
♣七:docker的私有registy
A:docker的私有registy介紹
為什么需要私有的鏡像倉庫,是因為docker的本意就是為了更加輕量快速的搭建起一整套的環境提供使用,如果你的鏡像文件是寄托於dockerhub之上的,那你不得不面臨pull鏡像的時候占用大量的帶寬和時間成本,這種場景是極為不適用於大量服務器的場景的,所以做可取的方案就是本地自建鏡像倉庫,當然你如果是用的國內的阿里雲等雲平台,把鏡像倉庫放在雲上面也是很不錯的選擇,要保證鏡像在被pull的時候效率提高,你必須選擇就近原則。而且你還得考慮雙機熱備或者多地存放的方案,隨時應對不必要的故障帶來的損失。
docker為了方便用戶創建私有的鏡像倉庫也提供了一個程序包docker-distribution,而且這個程序也被制作成了鏡像存在了duckerhub上,我們直接pull下來啟動就可以使用了。
我們要制作的鏡像是基於docker-distribution容器來完成的,docker-distribution容器一旦終止,上面之前你制作的所有鏡像也就沒有了,所以docker-distribution容器必須指定一個存儲卷,這個存儲卷最好是穩定的網絡存儲,這樣等於你制作鏡像的環境隨時可以做一些調整不至於影響制作好的鏡像,等於成品鏡像我是存到了一個穩定的存儲卷上面的。
B:docker的私有registy安裝和簡單使用
現在在dockerhub上docker-distribution的鏡像registry已經到了2.7.1的版本了,我們直接使用本地的yum倉庫來安裝。

[root@localhost ~]# yum info docker-registry 已加載插件:fastestmirror, langpacks Determining fastest mirrors * base: mirrors.aliyun.com * extras: mirrors.aliyun.com * updates: ap.stykers.moe 可安裝的軟件包 名稱 :docker-registry 架構 :x86_64 版本 :0.9.1 可以看到registry是0.9.1的版本,但是這個版本只是registry的版本,不是docker-distribution的版本,我們直接安裝這個版本。 發布 :7.el7 大小 :123 k 源 :extras/7/x86_64 簡介 : Registry server for Docker 網址 :https://github.com/docker/docker-registry 協議 : ASL 2.0 描述 : Registry server for Docker (hosting/delivering of repositories and images). [root@localhost ~]# yum -y install docker-registry ...... 正在安裝: docker-distribution x86_64 2.6.2-2.git48294d9.el7 extras 3.5 M 真正的docker-distribution可以看到是2.6.2的版本,docker-distribution只不過是封裝在docker-registry,所以安裝的時候還是要看實際的版本。 [root@localhost ~]# 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@localhost ~]# systemctl start docker-distribution啟動服務 [root@localhost ~]# cat /etc/docker-distribution/registry/config.yml version: 0.1 log: fields: service: registry storage: cache: layerinfo: inmemory 緩存數據在內存空間 filesystem: rootdirectory: /var/lib/registry 鏡像存儲路徑 http: 是一個http的服務 addr: :5000 默認端口5000,前面冒號之間是沒有指明地址的代表本地所有地址 [root@localhost ~]# ss -tnl State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128

[root@localhost ~]# docker tag mynginxweb:v1.5 localhost:5000/mynginxweb:v1.5 先給本地的鏡像打個標簽,localhost:5000因為我們本地機器沒有域名就直接指定本地5000端口就可以了,/mynginxweb:v1.5如果是名稱的就在斜杠后面加上名稱,沒有名稱就為頂層倉庫。 [root@localhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE test v1.1 22eefe8da83d 20 hours ago 16.4MB mynginxweb v1.5 d15a52fae68c 20 hours ago 16MB localhost:5000/mynginxweb v1.5 d15a52fae68c 20 hours ago 16MB [root@localhost ~]# docker push localhost:5000/mynginxweb:v1.5 如果推送的時候不指定版本默認是推送mynginxweb整個鏡像,所有這個tag很重要。 The push refers to repository [localhost:5000/mynginxweb] 355b0d8dba3a: Pushed 54393d713599: Pushed 129ba078f157: Pushed 8c8f1eccd524: Pushed 68442845474f: Pushed 503e53e365f3: Pushed v1.5: digest: sha256:3601b3c57d30876a61ef8c5b226bf091cd3ffa61b0eae50aba12a172705b67fa size: 1568 因為我們是本地直接推,不會出現https的報錯 [root@localhost yum.repos.d]# docker tag mynginxweb:v1.5 192.168.181.134:5000/mynginxweb:v1.5 [root@localhost yum.repos.d]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE test v1.1 22eefe8da83d 24 hours ago 16.4MB 192.168.181.134:5000/mynginxweb v1.5 d15a52fae68c 24 hours ago 16MB [root@localhost yum.repos.d]# docker push 192.168.181.134:5000/mynginxweb:v1.5 The push refers to repository [192.168.181.134:5000/mynginxweb] Get https://192.168.181.134:5000/v2/: http: server gave HTTP response to HTTPS client 當我們要把本地鏡像推送到另外的機器上的時候會報錯,因為我們推送是基於docker的https協議,但是對端的機器是http的協議,用https訪問http肯定會報錯,當然如果是內網的環境,無需使用https協議也是可以改成http的協議的 這個時候需要使用到一個參數添加到daemon.json里面,重啟docker就可以。 [root@localhost yum.repos.d]# cat /etc/docker/daemon.json { "registry-mirrors":["https://registry.docker-cn.com"], "insecure-registries":["192.168.181.134:5000"] 通過insecure-registries參數指定私有倉庫的地址或者域名,而且這個域名或者地址必須是docker images的鏡像REPOSITORY名稱。 } [root@localhost yum.repos.d]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE test v1.1 22eefe8da83d 25 hours ago 16.4MB 192.168.181.134:5000/mynginxweb v1.5 d15a52fae68c 25 hours ago 16MB daemon.json里面定義的insecure-registries必須和images REPOSITORY的指一致。 [root@localhost yum.repos.d]# !1048 (用感嘆號加上history出來的指令編號就能執行相應編號的指令 docker push 192.168.181.134:5000/mynginxweb:v1.5 The push refers to repository [192.168.181.134:5000/mynginxweb] 355b0d8dba3a: Pushed 54393d713599: Pushed 129ba078f157: Pushed 8c8f1eccd524: Pushed 68442845474f: Pushed 503e53e365f3: Pushed v1.5: digest: sha256:3601b3c57d30876a61ef8c5b226bf091cd3ffa61b0eae50aba12a172705b67fa size: 1568 可以看到推送成功了 [root@www repositories]# ls mynginxweb [root@www repositories]# pwd /var/lib/registry/docker/registry/v2/repositories [root@www repositories]#可以看到對端機器上/var/lib/registry/自動生成了我們鏡像相對應名字的文件。 [root@www repositories]# cd mynginxweb/ [root@www mynginxweb]# ls _layers(鏡像生成的層目錄,這個層就是之前的聯合掛載提到的層) _manifests _uploads(上傳目錄) [root@www sha256]# pwd /var/lib/registry/docker/registry/v2/repositories/mynginxweb/_layers/sha256 [root@www sha256]# ls 0833c98aa19af703feb3525ba6b7570c63148a9d8cf175711320281e431720dd 389a52582f93c5d83ca4015d27b0088bd84c256f0eac34810cae4d1508a05f1c 496e2dd2b91a449fbf89d6bb7ef65a85cbab272acca2ce39827cba99e4b2b943 6c40cc604d8e4c121adcb6b0bfe8bb038815c350980090e74aa5a6423f8f82c0 76679ad9f124f838aa51b98a34af7d0fa586fba6bf5ec05d452c7a714141180b d15a52fae68cb0b3ee2c734350046afea18f8d662a6791ac3767941f58a0fda1 f82709aa42ef56cffb4fac78bbbf03e2045faf5fe3f3bad82808bca1b251dd3c [root@www 0833c98aa19af703feb3525ba6b7570c63148a9d8cf175711320281e431720dd]# pwd /var/lib/registry/docker/registry/v2/repositories/mynginxweb/_layers/sha256/0833c98aa19af703feb3525ba6b7570c63148a9d8cf175711320281e431720dd [root@www 0833c98aa19af703feb3525ba6b7570c63148a9d8cf175711320281e431720dd]# ls link 可以看到這是一個連接文件,真正的文件是在blobs/sha256下面 [root@www sha256]# ls 08 36 38 49 6c 76 d1 f8 可以看到這每一個文件的前兩位都對應_layers/sha256下一長串的文件名 [root@www sha256]# pwd /var/lib/registry/docker/registry/v2/blobs/sha256 我們在134機器上把我們剛才推送的鏡像給pull下來 [root@www home]# cat /etc/docker/daemon.json { "registry-mirrors":["https://registry.docker-cn.com"], "insecure-registries":["192.168.181.134:5000"] 因為你pull也是要走網絡的,但是docker默認還是https協議,所以想用http下載下來還是要配置134機器daemon.json的配置文件。 } [root@www home]# docker pull 192.168.181.134:5000/mynginxweb:v1.5 指定要pull的鏡像和具體標簽。 v1.5: Pulling from mynginxweb 6c40cc604d8e: Pull complete 76679ad9f124: Pull complete 389a52582f93: Pull complete 496e2dd2b91a: Pull complete f82709aa42ef: Pull complete 0833c98aa19a: Pull complete Digest: sha256:3601b3c57d30876a61ef8c5b226bf091cd3ffa61b0eae50aba12a172705b67fa Status: Downloaded newer image for 192.168.181.134:5000/mynginxweb:v1.5 [root@www home]# docker images 可以看到134機器也pull下來了 REPOSITORY TAG IMAGE ID CREATED SIZE 192.168.181.134:5000/mynginxweb v1.5 d15a52fae68c 25 hours ago 16MB
如果需要參考docker官方的文檔可以訪問https://docs.docker.com/
一個簡單的本地私有倉庫的搭建推送下載過程就完成了,當然和dockerhub官方網站不能相比,能支持搜索等功能,其實docker是能支持web。
提到私有倉庫不得不提及到的就是CNCF,為了統一雲計算接口和相關標准,2015年7月隸屬於 Linux 基金會的雲原生計算基金會(CNCF)應運而生。談到 CNCF,首先它是一個非營利組織,致力於通過技術優勢和用戶價值創造一套新的通用容器技術,推動本土雲計算和服務的發展。CNCF關注於容器如何管理而不是如何創建,因為如果沒有一個成熟的平台去管理容器,那么大型企業無法真正放心接受並使用容器。
CNCF官方網站:https://www.cncf.io/
CNCF的明星項目就有k8s,Prometheus(專門為雲原生提供實時監控的工具),這里面就包含了一個私有倉庫服務器軟件Harbor,Harbor也是基於docker-registry來做的二次開發,加進去了很多功能,就包含了web的界面
在未來的計算機服務環境會變的越來越復雜,一旦復雜就需要我們把復雜的程序拆解成很多不同的子母單元,眾多的子母單元或者模塊每天都可能遇到網絡抖動出現的延遲,硬件的故障,軟件的bug等等出現的一系列問題導致的服務中斷,這眾多的子母單元的關聯和問題的解決還是運維去手動維護幾乎是不可能的,一定通過工具來完成。
當你需要快速的啟動一環境的時候需要使用k8s的編排工具,那么環境的基礎鏡像又是要基於docker來完成,docker制作的鏡像大多數情況下還是存在私有的registry中,那么私有的registry必然會用到Harbor。目前通過k8s可以實現自動的擴縮容,問題的自動修復,灰度,金絲雀的發布等功能部分已經能實現,那么在未來k8s和Harbor必然會成為主流。
Harbor在單機部署的時候會很麻煩,需要redis等程序的聯合使用,所以Harbor也是有鏡像的,而且這個鏡像是基於docker compose(單機)編排工具來完成的
https://docs.docker.com/compose/
C:Harbor安裝和簡單使用
Harbor的官網:https://goharbor.io/
github的項目網站:https://github.com/goharbor/harbor/releases
下載harbor的時候需要看下版本相關的文檔,避免依賴的環境不能滿足安裝的需求。

[root@www home]# wget https://storage.googleapis.com/harbor-releases/release-1.6.0/harbor-offline-installer-v1.6.3.tgz --2019-03-31 20:32:14-- https://storage.googleapis.com/harbor-releases/release-1.6.0/harbor-offline-installer-v1.6.3.tgz 正在解析主機 storage.googleapis.com (storage.googleapis.com)... 216.58.197.112, 2404:6800:4005:809::2010 正在連接 storage.googleapis.com (storage.googleapis.com)|216.58.197.112|:443... 已連接。 已發出 HTTP 請求,正在等待回應... 200 OK 長度:659737336 (629M) [application/x-tar] 正在保存至: “harbor-offline-installer-v1.6.3.tgz” 100%[=====================================================================================>] 659,737,336 593KB/s 用時 39m 23s 2019-03-31 21:11:38 (273 KB/s) - 已保存 “harbor-offline-installer-v1.6.3.tgz” [659737336/659737336]) 因為文件比較大,下載會比較慢 下載之后解壓然后稍微修改下配置文件: hostname = www.distribution.com 改成本機的主機名或者ip max_job_workers = 2 並行運行的進程數量安好環境來適當分派 log_rotate_size = 200M 日志超過200M就需要進行滾動了 harbor_admin_password = 1q2w3e4r 管理員的密碼默認是Harbor123,我們改一下 保存安裝 [root@www harbor]# ./install.sh [Step 0]: checking installation environment ... Note: docker version: 18.09.4 ? Need to install docker-compose(1.7.1+) by yourself first and run this script again. 執行安裝腳本的時候會提醒需要安裝docker-compose(1.7.1+)或以上的版本,docker-compose是在epel源里面的,可以通過阿里雲的倉庫去安裝epel源 [root@www harbor]# ./install.sh 執行install腳本 [Step 0]: checking installation environment ... Note: docker version: 18.09.4 Note: docker-compose version: 1.18.0 [Step 1]: loading Harbor images ... 4de51055f30c: Loading layer [==================================================>] 133.2MB/133.2MB e0571faef8b9: Loading layer [==================================================>] 23.38MB/23.38MB af771ff8e9c2: Loading layer [==================================================>] 26.88MB/26.88MB 23af616ab835: Loading layer [==================================================>] 7.168kB/7.168kB bc63d511139a: Loading layer [==================================================>] 11.32MB/11.32MB c58849213c99: Loading layer [==================================================>] 26.87MB/26.87MB Loaded image: goharbor/harbor-ui:v1.6.3 69a7338cd264: Loading layer [==================================================>] 23.38MB/23.38MB f90eaaa05b41: Loading layer [==================================================>] 21.15MB/21.15MB 44d04c9c1fb6: Loading layer [==================================================>] 21.15MB/21.15MB Loaded image: goharbor/harbor-jobservice:v1.6.3 f3046fec63a0: Loading layer [==================================================>] 23.38MB/23.38MB bb409c51bc96: Loading layer [==================================================>] 10.95MB/10.95MB .....可以看到裝載了很多鏡像。 [Step 3]: checking existing instance of Harbor ... Creating registry ... done Creating harbor-ui ... done Creating network "harbor_harbor" with the default driver Creating nginx ... done Creating harbor-db ... Creating harbor-adminserver ... Creating registry ... Creating redis ... Creating harbor-ui ... Creating harbor-jobservice ... Creating nginx ... ? ----Harbor has been installed and started successfully.---- Now you should be able to visit the admin portal at http://www.distribution.com. For more details, please visit https://github.com/goharbor/harbor . [root@www harbor]# ss -tnl State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 127.0.0.1:1514 *:* LISTEN 0 128 *:111 *:* LISTEN 0 128 *:6000 *:* LISTEN 0 5 192.168.122.1:53 *:* LISTEN 0 128 *:22 *:* LISTEN 0 128 127.0.0.1:631 *:* LISTEN 0 100 127.0.0.1:25 *:* LISTEN 0 128 127.0.0.1:6010 *:* LISTEN 0 128 127.0.0.1:6011 *:* LISTEN 0 128 :::111 :::* LISTEN 0 128 :::80 :::* LISTEN 0 128 :::6000 :::* LISTEN 0 128 :::22 :::* LISTEN 0 128 ::1:631 :::* LISTEN 0 100 ::1:25 :::* LISTEN 0 128 ::1:6010 :::* LISTEN 0 128 :::443 :::* LISTEN 0 128 :::4443 :::* LISTEN 0 128 ::1:6011 :::* 同時監聽了物理機的80,443和4443的端口 安裝完成之后就啟動了,可以登錄網頁進行訪問了。 使用admin登錄網站
使用在harbor.cfg配置文件里面定義好的密碼登錄harbor。
創建用戶和倉庫
harbor原生支持鏡像的復制,只需要配置規則就能把一個harbor上鏡像復制到其它的備份環境。
支持配置郵件,誰上傳了鏡像下載了鏡像都可以通過郵件通知
可以看到harbor能提示你給要push的鏡像打包的命令和push的方法。

[root@www harbor]# pwd /home/harbor/harbor [root@www harbor]# ls common docker-compose.clair.yml docker-compose.yml harbor.cfg install.sh NOTICE prepare docker-compose.chartmuseum.yml docker-compose.notary.yml ha harbor.v1.6.3.tar.gz LICENSE open_source_license 到docker-compose.yml所在的路徑下執行相應的命令即可 [root@www harbor]# docker-compose stop Stopping harbor-log ... done [root@www harbor]# docker-compose start Starting log ... done Starting registry ... done Starting redis ... done Starting adminserver ... done Starting postgresql ... done Starting ui ... done Starting jobservice ... done Starting proxy ... done [root@www harbor]# docker-compose --help Commands: build Build or rebuild services bundle Generate a Docker bundle from the Compose file config Validate and view the Compose file create Create services down Stop and remove containers, networks, images, and volumes events Receive real time events from containers exec Execute a command in a running container help Get help on a command images List images kill Kill containers logs View output from containers pause Pause services port Print the public port for a port binding ps List containers pull Pull service images push Push service images restart Restart services rm Remove stopped containers run Run a one-off command scale Set number of containers for a service start Start services stop Stop services top Display the running processes unpause Unpause services up Create and start containers version Show the Docker-Compose version information [root@www harbor]#

[root@www harbor]# docker push 192.168.181.134/mynginxweb/mynginxweb:v1.1 The push refers to repository [192.168.181.134/mynginxweb/mynginxweb] 355b0d8dba3a: Preparing 54393d713599: Preparing 129ba078f157: Preparing 8c8f1eccd524: Preparing 68442845474f: Preparing 503e53e365f3: Waiting denied: requested access to the resource is denied 如果沒有登錄的話是不能推送鏡像到倉庫的,需要登錄做認證之后才能推送 [root@www harbor]# docker login 192.168.181.134 Username: harbor 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 harbor]# docker push 192.168.181.134/mynginxweb/mynginxweb:v1.1 The push refers to repository [192.168.181.134/mynginxweb/mynginxweb] 355b0d8dba3a: Pushed 54393d713599: Pushed 129ba078f157: Pushed 8c8f1eccd524: Pushed 68442845474f: Pushed 503e53e365f3: Pushed v1.1: digest: sha256:3601b3c57d30876a61ef8c5b226bf091cd3ffa61b0eae50aba12a172705b67fa size: 1568 登錄之后推送就沒有問題了 [root@www harbor]#
可以看到倉庫就能查詢到我們剛才推送的鏡像了。
♣八:portainer的安裝和使用
A:portainer安裝和簡單使用
portainer是一個強大的docker容器,鏡像管理web平台,能更加直觀的看到容器的相關信息,例如資源信息,卷,網路,通過網頁快速鏈接容器執行相應的命令等。更加方便。
portainer官方網站:https://www.portainer.io/products-services/
portainer支持linux和windows平台。
portainer安裝極為簡單,可以參照官方文檔進行安裝:https://www.portainer.io/installation/,當然前提是你安裝了docker並啟動了。

$ docker volume create portainer_data $ docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer [root@www home]# ss -tnl | grep 9000 LISTEN 0 128 :::9000 :::* [root@www home]# 監聽的是9000端口,然后就可以通過網頁訪問了。
安裝之后登陸界面會提示先注冊admin用戶,然后還可以選擇是本地的還是遠程的形式去管理,登陸界面之后我們就能看到服務器上的相關信息了,菜單欄分為三大塊,1:是home目錄,匯總概括顯示機器相關的信息,2:容器相關每一個具體信息的查詢菜單,3:設置項菜單,可以創建用戶,用戶組,添加擴展,做快照,添加模板等功能。
可以快速查詢當前容器的資源使用情況,使用什么用戶執行了什么命令等。
還可以通過sh環境去連接容器執行命令
方便用戶快速鏈接容器執行命令。
快速查看容器的相關信息,和在命令行執行docker info的命令結果類似。
可以看書查看容器的dockerfile的編寫命令。
方便快速查看容器的網絡。
至於portainer更多的使用方法可以參照官方的文檔來查詢:https://www.portainer.io/overview/