Dockerfile和docker-compose使用詳解
Dockerfile和docker-compose詳解
一、Dockerfile
1. Dockerfile簡介
Dockerfile是一個(gè)用來構(gòu)建鏡像的文本文件, 文本內(nèi)容包含了一條條構(gòu)建鏡像所需的指令和說明。
例如我們要在含python3的centos鏡像基礎(chǔ)上安裝vim,可以這么寫。
FROM centos:python3 RUN yum -y install vim
這里的FROM表示從哪個(gè)鏡像開始構(gòu)建,可以是本地已有的鏡像,也可以是dockerhub或者私域的鏡像。
2. 構(gòu)建鏡像
在Dockerfile文件的存放目錄下, 執(zhí)行構(gòu)建動(dòng)作
# 1. nginx表示鏡像名稱, v3表示版本 # 2. 最后的.表示Dockerfile相對終端執(zhí)行環(huán)境的相對路徑 docker build -t centos:python3 .
3. Dockerfile命令
(1)FROM
定制的鏡像都是基于FROM的鏡像, 后續(xù)的操作都是基于該鏡像做的操作
(2)WORKDIR
官方文檔的說明,WORKDIR的作用是為RUN, COPY等指令設(shè)置工作目錄,相當(dāng)于cd到那個(gè)目錄,然后執(zhí)行對應(yīng)的指令。
如果沒有設(shè)置WORKDIR,它會(huì)自動(dòng)創(chuàng)建,默認(rèn)是/,如果是從其他鏡像開始構(gòu)建的,那么WORKDIR就是其他鏡像。
The WORKDIR instruction sets the working directory for any RUN, CMD, ENTRYPOINT, COPY and ADD instructions that follow it in the Dockerfile. If the WORKDIR doesn’t exist, it will be created even if it’s not used in any subsequent Dockerfile instruction.
WORKDIR /a WORKDIR b WORKDIR c RUN pwd 輸出就是/a/b/c
(3)RUN
用于執(zhí)行后面跟著的命令行命令
RUN <命令行命令> # 命令行命令 等同于在終端操作的shell命令
例如前面的
RUN yum -y install vim
注意,在RUN命令中有路徑時(shí),指的是容器外的相對路徑,而不是容器內(nèi)的路徑,因?yàn)樗藭r(shí)還只是構(gòu)建鏡像。
(4)COPY
將宿主機(jī)的文件拷貝到鏡像中。
由于它是構(gòu)建鏡像時(shí)的命令,因此它會(huì)將文件寫入到鏡像中,只要是由該鏡像創(chuàng)建的容器,都會(huì)有拷貝過去的文件,這是它和掛載的不同。
FROM centos:vim-python3 RUN yum -y install vim COPY python/ /lkidti/ CMD python3 /lkidti/http_server.py
- 源路徑src:宿主機(jī)內(nèi)的路徑,是相對Dockerfile的路徑,不能用絕對路徑。例如寫成/home/lkidti/python,這種寫法是不對的,因?yàn)閐ockerfile會(huì)把它翻譯成 dockerfile路徑+/home/lkidti/python。
- 目的路徑dest:容器內(nèi)的路徑,用的是絕對路徑,或者是相對workdir的路徑。
(5)EXPOSE
暴露端口,這個(gè)命令的作用參見官方文檔。
The EXPOSE instruction does not actually publish the port. It functions as a type of documentation between the person who builds the image and the person who runs the container, about which ports are intended to be published
expose命令的作用并不會(huì)去暴露端口,而是作為構(gòu)建鏡像的開發(fā)人員和運(yùn)行容器的開發(fā)人員之間一個(gè)“接口文檔”,構(gòu)建鏡像的開發(fā)人員告訴運(yùn)行容器的開發(fā)人員,該鏡像監(jiān)聽哪個(gè)端口。
因?yàn)閷τ谶\(yùn)行容器的開發(fā)人員來說,它可能接觸不到源碼,不清楚該鏡像能暴露哪個(gè)端口,expose命令能幫助他了解到。
例如redis鏡像。
[lkidti@hecs-300320 ~]$ docker run -id redis:latest Unable to find image 'redis:latest' locally latest: Pulling from library/redis c7ae2cc6d9d5d2fee1aefbea14014bb42806b45c60b7d6a1cf3313d5367ae895
[lkidti@hecs-300320 ~]$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c7ae2cc6d9d5 redis:latest "docker-entrypoint.s…" 5 seconds ago Up 3 seconds 6379/tcp awesome_cori
它這里就暴露了6379端口,因此我們需要在運(yùn)行的時(shí)候通過-p來做端口映射,來保證通過訪問宿主機(jī)來訪問容器。
(6)CMD
在鏡像構(gòu)建好后,用鏡像啟動(dòng)容器時(shí)(docker run)會(huì)執(zhí)行的命令。
當(dāng)在Dockerfile中寫了CMD時(shí),如果在用docker run或者docker-compose啟動(dòng)容器時(shí),又再加了啟動(dòng)命令,此時(shí)執(zhí)行的是docker run或者docker-compose的命令,如果沒有加,執(zhí)行的就是Dockerfile中的命令。
- 例子1:docker run加了命令/bin/bash
[lkidti@hecs-300320 ~]$ docker run -id centos:python-vim /bin/bash 9a25fca7d79046bf693e95e2836744a5d973b12dc25adefe9b78e7e56e56df8f [lkidti@hecs-300320 ~]$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9a25fca7d790 centos:python-vim "/bin/bash" 4 seconds ago Up 3 seconds nice_mendel 33d744b8729a mysql:latest "docker-entrypoint.s…" 8 hours ago Up 8 hours 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp mysql
- 例子2:docker run中不加命令
[lkidti@hecs-300320 ~]$ docker run -id centos:python-vim dba629df688a0fb326d7e1c668fe4393673ca3f1789dd7a9e666fcd9344990d7 [lkidti@hecs-300320 ~]$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES dba629df688a centos:python-vim "/bin/sh -c 'python3…" 5 seconds ago Up 4 seconds vigorous_solomon 33d744b8729a mysql:latest "docker-entrypoint.s…" 8 hours ago Up 8 hours 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp mysql
二、docker-compose
1.簡介
用于構(gòu)建和啟動(dòng)多容器工具, 通過docker-compose.yml來配置項(xiàng)目需要的所有服務(wù), 然后docker-compose up啟動(dòng)所有服務(wù)
2.多服務(wù)實(shí)例
(1)文件準(zhǔn)備
新建一個(gè)目錄:
mkdir composetest cd composetest
新建一個(gè)app.py文件
# composetest/app.py import time ? import redis from flask import Flask ? app = Flask(__name__) cache = redis.Redis(host='redis', port=6379) ? def get_hit_count(): retries = 5 while True: try: return cache.incr('hits') except redis.exceptions.ConnectionError as exc: if retries == 0: raise exc retries -= 1 time.sleep(0.5) ? @app.route('/') def hello(): count = get_hit_count() return 'Hello World! I have been seen {} times.\n'.format(count)
新建一個(gè)requirements.txt,里面包含如下內(nèi)容
# composetest/requirements.txt flask redis
新建一個(gè)Dockerfile
# syntax=docker/dockerfile:1 FROM python:3.7-alpine WORKDIR /code ENV FLASK_APP=app.py ENV FLASK_RUN_HOST=0.0.0.0 RUN apk add --no-cache gcc musl-dev linux-headers COPY requirements.txt requirements.txt RUN pip install -r requirements.txt EXPOSE 5000 COPY . . CMD ["flask", "run"]
新建一個(gè)docker-compose.yml
services: web: build: . ports: - "8000:5000" redis: image: "redis:alpine"
(2)啟動(dòng)服務(wù)
docker compose up # 注意,最新版本可以不要中間的"-"
此時(shí)應(yīng)該會(huì)看到兩個(gè)服務(wù)都啟動(dòng)起來了
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b69ccfb85c03 composetest-web "flask run" 12 minutes ago Up 12 minutes 0.0.0.0:8000->5000/tcp, :::8000->5000/tcp composetest-web-1 ea71df663f36 redis:alpine "docker-entrypoint.s…" 12 minutes ago Up 12 minutes 6379/tcp composetest-redis-1
(3)訪問服務(wù)
在服務(wù)器內(nèi)部執(zhí)行
curl http://127.0.0.1:5000
可以看到輸出
Hello World! I have been seen 1 times.
由于是在服務(wù)器上部署,還可以在瀏覽器中訪問http://121.36.104.55:8000/,也是一樣的輸出。
注意,要開放8000端口。
3. docker-compose的service
(1)service和container
在上面的例子,web實(shí)際上指的是一個(gè)服務(wù),而不是一個(gè)容器。參考https://stackoverflow.com/a/35585573/10844937
A service can be run by one or multiple containers. With docker you can handle containers and with docker-compose you can handle services.
service可以由一個(gè)或多個(gè)container組成。
docker一般來操作container,docker-compose一般來操作service。
可以在docker- compose中指定scale參數(shù)來指定container的個(gè)數(shù),例如
services: web: build: . scale: 4 redis: image: "redis:alpine"
此時(shí)會(huì)有4個(gè)container
? composetest git:(master) ? docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f17412bbb248 composetest-web "flask run" 15 seconds ago Up 5 seconds 5000/tcp composetest-web-4 ded649c4cec8 composetest-web "flask run" 15 seconds ago Up 5 seconds 5000/tcp composetest-web-3 642b05b9c66e composetest-web "flask run" 15 seconds ago Up 5 seconds 5000/tcp composetest-web-2 7732ae7c78c8 composetest-web "flask run" 16 seconds ago Up 5 seconds 5000/tcp composetest-web-1 1cf0b1c34972 redis:alpine "docker-entrypoint.s…" 10 minutes ago Up 10 minutes 6379/tcp composetest-redis-1
在docker-compose中,必須有service name,而不必有container name,如果沒有container name,那么container name=<當(dāng)前工作路徑名>,這里的sequence number是從1開始的。
(2)service的廣泛應(yīng)用
非常重要的一點(diǎn): service name 可以廣泛的被應(yīng)用
在nginx中,可以看到proxy_pass http://web:8000;這樣的表示式,這里的web就是指的service name
在django的settings中,可以看到數(shù)據(jù)的配置如下,這里的db指的也是service name
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'orthanc', 'HOST': 'db', 'PORT': 5432, 'USER': 'lkidti', 'PASSWORD': '4a53e4f5c42fd5a31890860b204472c5' } }
4. docker-compose的指令
(1)image
如果本地有鏡像, 直接用本地鏡像; 如果沒有, 采用dockerhub的。
version: "3.7" services: webapp: build: context: ./dir # Dockerfile的路徑 dockerfile: Dockerfile-alternate # Dockerfile的名字 args: buildno: 1 # Dockerfile構(gòu)建鏡像時(shí)候的參數(shù),在構(gòu)建時(shí)候的環(huán)境變量
(2)build
build: 是指通過Dockerfile來構(gòu)建。當(dāng)一個(gè)yaml文件中,既有image又有build時(shí),它的順序如下:
首先看本地是否有鏡像,如果有,用本地的鏡像
本地如果沒有,嘗試從dockerhub拉。
如果dockerhub拉不到,則用build參數(shù)中指定的Dockerfile來構(gòu)建鏡像。
services: backend: image: awesome/database build: context: backend # 相對docker-compose的子目錄 dockerfile: ../backend.Dockerfile # dockerfile文件名
(3)depends_on
告訴docker-compose當(dāng)前服務(wù)啟動(dòng)之前先要把depends_on指定的服務(wù)啟動(dòng)起來才行
(4)environment
environment變量可以在構(gòu)建鏡像過程中,在Dockerfile中去使用。
也可以在已構(gòu)建好的鏡像制作出的容器中使用,在容器的終端中輸入env即可查找到所有的環(huán)境變量。
可以用如下的python代碼拿到具體的環(huán)境變量。
import os os.environ.get('DEBUG')
(5)volume
docker的掛載主要有兩種方式
- bind mount(全路徑的主機(jī)目錄):將主機(jī)的目錄mount到container中,這種方式主機(jī)的目錄路徑必須為全路徑,否則docker會(huì)將其當(dāng)做volume處理。這種方式有一個(gè)不好的地方: windows和linux的目錄結(jié)構(gòu)不一樣,那么此時(shí)我們是沒法在不同的系統(tǒng)去寫一個(gè)主機(jī)的目錄來兼容的。
- volume(非全路徑的主機(jī)目錄):volume和bind mount不同之處在于,volume的主機(jī)目錄是被docker管理的,都在主機(jī)的/var/lib/docker/volumes目錄下,這個(gè)目錄的權(quán)限非常嚴(yán)格,即使是用sudo都不能打開(cd)。將my-volume掛載到container中的/mydata目錄: docker run -it -v my-volume:/mydata alpine sh,它會(huì)在主機(jī)下創(chuàng)建/var/lib/docker/volumes/my-volume/_data目錄,如果該目錄不存在,那么docker會(huì)先創(chuàng)建然后再掛載。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
如何解決安裝docker的yum工具時(shí)報(bào)錯(cuò)問題
在安裝Docker時(shí)遇到y(tǒng)um工具錯(cuò)誤可通過更新yum源解決,先卸載舊Docker,備份原yum源,下載新的CentOS-Base.repo文件到指定目錄,安裝yum工具后,配置Docker的yum源,國內(nèi)用戶建議使用aliyun源以避免訪問異常,安裝并啟動(dòng)Docker,校驗(yàn)是否成功2024-09-09IDEA 通過docker插件發(fā)布springboot項(xiàng)目的詳細(xì)教程
這篇文章主要介紹了IDEA 通過docker插件發(fā)布springboot項(xiàng)目的詳細(xì)教程,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09Docker部署Vue項(xiàng)目的項(xiàng)目實(shí)踐
本文主要介紹了Docker部署Vue項(xiàng)目的項(xiàng)目實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07利用Docker分層構(gòu)建優(yōu)化鏡像大小的實(shí)現(xiàn)
合適docker鏡像文件大小不僅影響容器啟動(dòng)效率,也影響資源占用效率,本文介紹如何利用分層方式構(gòu)建docker鏡像,采用多種方式避免鏡像文件太大而影響性能,需要的朋友可以參考下2025-01-01替換docker容器中的一個(gè)文件的實(shí)現(xiàn)
在某些情況下,我們可能確實(shí)需要更新容器內(nèi)的文件,本文主要介紹了替換docker容器中的一個(gè)文件的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2024-06-06解決vscode docker插件docker.socket權(quán)限問題
本文給大家分享關(guān)于vscode docker插件docker.socket權(quán)限問題,文末給大家提到vscode中docker插件無法連接的問題及解決方案,需要的朋友參考下吧2021-06-06阿里云docker容器固定應(yīng)用到到某一個(gè)節(jié)點(diǎn)記錄
這篇文章主要介紹了阿里云docker容器固定應(yīng)用到到某一個(gè)節(jié)點(diǎn)記錄,需要的朋友可以參考下2018-05-05Docker Volumn容器間共享數(shù)據(jù)的實(shí)現(xiàn)
這篇文章主要介紹了Docker Volumn容器間共享數(shù)據(jù)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01