Dockerfile和docker-compose使用詳解
Dockerfile和docker-compose詳解
一、Dockerfile
1. Dockerfile簡介
Dockerfile是一個用來構(gòu)建鏡像的文本文件, 文本內(nèi)容包含了一條條構(gòu)建鏡像所需的指令和說明。
例如我們要在含python3的centos鏡像基礎(chǔ)上安裝vim,可以這么寫。
FROM centos:python3 RUN yum -y install vim
這里的FROM表示從哪個鏡像開始構(gòu)建,可以是本地已有的鏡像,也可以是dockerhub或者私域的鏡像。
2. 構(gòu)建鏡像
在Dockerfile文件的存放目錄下, 執(zhí)行構(gòu)建動作
# 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到那個目錄,然后執(zhí)行對應(yīng)的指令。
如果沒有設(shè)置WORKDIR,它會自動創(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命令中有路徑時,指的是容器外的相對路徑,而不是容器內(nèi)的路徑,因?yàn)樗藭r還只是構(gòu)建鏡像。
(4)COPY
將宿主機(jī)的文件拷貝到鏡像中。
由于它是構(gòu)建鏡像時的命令,因此它會將文件寫入到鏡像中,只要是由該鏡像創(chuàng)建的容器,都會有拷貝過去的文件,這是它和掛載的不同。
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會把它翻譯成 dockerfile路徑+/home/lkidti/python。
- 目的路徑dest:容器內(nèi)的路徑,用的是絕對路徑,或者是相對workdir的路徑。
(5)EXPOSE
暴露端口,這個命令的作用參見官方文檔。
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命令的作用并不會去暴露端口,而是作為構(gòu)建鏡像的開發(fā)人員和運(yùn)行容器的開發(fā)人員之間一個“接口文檔”,構(gòu)建鏡像的開發(fā)人員告訴運(yùn)行容器的開發(fā)人員,該鏡像監(jiān)聽哪個端口。
因?yàn)閷τ谶\(yùn)行容器的開發(fā)人員來說,它可能接觸不到源碼,不清楚該鏡像能暴露哪個端口,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)行的時候通過-p來做端口映射,來保證通過訪問宿主機(jī)來訪問容器。
(6)CMD
在鏡像構(gòu)建好后,用鏡像啟動容器時(docker run)會執(zhí)行的命令。
當(dāng)在Dockerfile中寫了CMD時,如果在用docker run或者docker-compose啟動容器時,又再加了啟動命令,此時執(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)建和啟動多容器工具, 通過docker-compose.yml來配置項(xiàng)目需要的所有服務(wù), 然后docker-compose up啟動所有服務(wù)
2.多服務(wù)實(shí)例
(1)文件準(zhǔn)備
新建一個目錄:
mkdir composetest cd composetest
新建一個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)
新建一個requirements.txt,里面包含如下內(nèi)容
# composetest/requirements.txt flask redis
新建一個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"]
新建一個docker-compose.yml
services: web: build: . ports: - "8000:5000" redis: image: "redis:alpine"
(2)啟動服務(wù)
docker compose up # 注意,最新版本可以不要中間的"-"
此時應(yīng)該會看到兩個服務(wù)都啟動起來了
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í)際上指的是一個服務(wù),而不是一個容器。參考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可以由一個或多個container組成。
docker一般來操作container,docker-compose一般來操作service。
可以在docker- compose中指定scale參數(shù)來指定container的個數(shù),例如
services: web: build: . scale: 4 redis: image: "redis:alpine"
此時會有4個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ù),在構(gòu)建時候的環(huán)境變量
(2)build
build: 是指通過Dockerfile來構(gòu)建。當(dāng)一個yaml文件中,既有image又有build時,它的順序如下:
首先看本地是否有鏡像,如果有,用本地的鏡像
本地如果沒有,嘗試從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ù)啟動之前先要把depends_on指定的服務(wù)啟動起來才行
(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會將其當(dāng)做volume處理。這種方式有一個不好的地方: windows和linux的目錄結(jié)構(gòu)不一樣,那么此時我們是沒法在不同的系統(tǒng)去寫一個主機(jī)的目錄來兼容的。
- volume(非全路徑的主機(jī)目錄):volume和bind mount不同之處在于,volume的主機(jī)目錄是被docker管理的,都在主機(jī)的/var/lib/docker/volumes目錄下,這個目錄的權(quán)限非常嚴(yán)格,即使是用sudo都不能打開(cd)。將my-volume掛載到container中的/mydata目錄: docker run -it -v my-volume:/mydata alpine sh,它會在主機(jī)下創(chuàng)建/var/lib/docker/volumes/my-volume/_data目錄,如果該目錄不存在,那么docker會先創(chuàng)建然后再掛載。
總結(jié)
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
IDEA 通過docker插件發(fā)布springboot項(xiàng)目的詳細(xì)教程
這篇文章主要介紹了IDEA 通過docker插件發(fā)布springboot項(xiàng)目的詳細(xì)教程,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09Docker部署Vue項(xiàng)目的項(xiàng)目實(shí)踐
本文主要介紹了Docker部署Vue項(xiàng)目的項(xiàng)目實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07利用Docker分層構(gòu)建優(yōu)化鏡像大小的實(shí)現(xiàn)
合適docker鏡像文件大小不僅影響容器啟動效率,也影響資源占用效率,本文介紹如何利用分層方式構(gòu)建docker鏡像,采用多種方式避免鏡像文件太大而影響性能,需要的朋友可以參考下2025-01-01替換docker容器中的一個文件的實(shí)現(xiàn)
在某些情況下,我們可能確實(shí)需要更新容器內(nèi)的文件,本文主要介紹了替換docker容器中的一個文件的實(shí)現(xiàn),具有一定的參考價值,感興趣的可以了解一下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)用到到某一個節(jié)點(diǎn)記錄
這篇文章主要介紹了阿里云docker容器固定應(yīng)用到到某一個節(jié)點(diǎn)記錄,需要的朋友可以參考下2018-05-05Docker Volumn容器間共享數(shù)據(jù)的實(shí)現(xiàn)
這篇文章主要介紹了Docker Volumn容器間共享數(shù)據(jù)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01