Controlla e monitora i tuoi container Docker con Python
Usa Python per controllare i tuoi container docker! Ecco una procedura dettagliata su come iniziare. Si basa su un test di tre pacchetti popolari per la gestione dei dati tabulari.
Deposito Git:https://github.com/martinkarlssonio/polars-pandas-spark
Se desideri leggere l'aspetto dei dati di questo test, c'è una storia separata per questo!
/@martinkarlsson.io/pandas-spark-and-polars-quando-usare-quale-f4e85d909c6f
Esplora il codice
L'immagine sotto illustra l'idea generale con il codice. L'immagine di build del codice main.py avvia, controlla e monitora 12 contenitori Docker.
Controlla le tue immagini e i tuoi contenitori
La star di questa soluzione è il pacchetto Docker Python. In un requirements.txt (installato con pip) specifichiamo il pacchetto docker.
docker==6.0.1
import docker
dockerClient = docker.DockerClient()
Ottenere un elenco di tutti i contenitori. Qui il codice filtra anche i contenitori creati con un determinato nome di immagine.
def getContainers():
containersReturn = []
containers = dockerClient.containers.list(all=True)
for container in containers:
if imageName in str(container.image):
containersReturn.append(container)
return containersReturn
Elimina un elenco di contenitori (sempre una buona idea prima di iniziare una nuova esecuzione del test).
def removeContainers():
containers = getContainers()
for container in containers:
if imageName in str(container.image):
print("################################################### Deleting old container {}".format(container.name))
try:
container.stop()
container.remove()
except Exception as e:
print("################################################### Error deleting old container {}".format(container.name))
print(e)
Avvia un nuovo contenitore da un'immagine esistente o crea l'immagine se non esiste già. Passiamo anche un set di variabili di ambiente all'avvio del contenitore.
def runContainer(testType,dataframeN):
images = dockerClient.images.list(all=True)
if imageName in ' '.join(map(str, images)):
print("################################################### Image exist, starting container..")
dockerClient.containers.run(imageName+":latest", environment = {"TEST_TYPE":testType,"DATAFRAME_N":dataframeN,"CALC_N":calcN})
else:
print("################################################### Image doesn't exist, need to create it!")
dockerClient.images.build(path = "./", tag = imageName)
dockerClient.containers.run(imageName+":latest", environment = {"TEST_TYPE":testType,"DATAFRAME_N":dataframeN,"CALC_N":calcN})
#UBUNTU AS BASE
FROM ubuntu:focal
##UPDATE
RUN apt-get update -y
RUN apt-get install nano
RUN apt-get update -y
##PYTHON
RUN apt-get install -y python3-pip python3-dev
RUN pip3 install --upgrade pip
COPY requirements-container.txt /requirements.txt
RUN pip3 install -r requirements.txt
##JAVA
RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get -y install tzdata
RUN apt install -y openjdk-8-jdk
##COPY IN FILES
COPY test.py /test.py
CMD python3 /test.py
Ecco una funzione per monitorare la CPU, la memoria e il tempo di esecuzione utilizzando il comando 'stats' di Dockers.
Il calcolo dell'utilizzo della CPU potrebbe richiedere ulteriori spiegazioni. Confronta l'utilizzo della CPU dell'ultima lettura con quello corrente e lo confronta con l'utilizzo della CPU del sistema moltiplicato per la quantità di core.
Ecco la logica scritta in parole:
cpuDelta = Total CPU Usage (now) - Total Usage (last read)
systemDelta = System CPU Usage (now) - System CPU Usage (last read)
cpuPercent = (cpuDelta/systemDelta) * CPU Cores * 100
def dockerLog(event,testType,dataframeN):
cpuLog = []
memLog = []
print("################################################### dockerLog started")
startFlag = True
while True:
try:
containers = getContainers()
for container in containers:
status = container.stats(decode=None, stream = False)
try:
# Calculate the change for the cpu usage of the container in between readings
# Taking in to account the amount of cores the CPU has
cpuDelta = status["cpu_stats"]["cpu_usage"]["total_usage"] - status["precpu_stats"]["cpu_usage"]["total_usage"]
systemDelta = status["cpu_stats"]["system_cpu_usage"] - status["precpu_stats"]["system_cpu_usage"]
#print("systemDelta: "+str(systemDelta)+" cpuDelta: "+str(cpuDelta))
cpuPercent = (cpuDelta / systemDelta) * (status["cpu_stats"]["online_cpus"]) * 100
cpuPercent = int(cpuPercent)
#print("cpuPercent: "+str(cpuPercent)+"%")
#Fetch the memory consumption for the container
mem = status["memory_stats"]["usage"]
mem = int(mem/1000000)
if startFlag == True and cpuPercent == 0: #Not logging CPU increase during startup of container, before test code executes.
startFlag = False
startEpoch = int(time.time()*1000)
print("Startflag set to False - let's go!")
if startFlag == False:
cpuLog.append(cpuPercent)
memLog.append(mem)
except Exception as e:
print("Error: "+str(e))
status = container.stats(decode=None, stream = False)
break
except Exception as e:
print("Error: "+str(e))
pass
if event.is_set():
break
# Write the logging to a file
endEpoch = int(time.time()*1000)
with open("output/"+str(dataframeN)+"_"+testType+"_"+containerStatsName, "w") as f:
json.dump({"mem":memLog,"cpu":cpuLog,"timeSpent":float((endEpoch-startEpoch)/1000)}, f)
print("################################################### dockerLog ended")
Vorremmo eseguire la funzione dockerLog in un thread separato, quindi avviamo la funzione utilizzando il pacchetto 'threading'. Ciò significa che non bloccheremo l'esecuzione del codice e potremo in parallelo avviare il contenitore.
from threading import Thread
from threading import Event
event = Event()
dockerLogThread = Thread(target=dockerLog, args=(event,testType,dataframeN,))
dockerLogThread.start()
runContainer(testType,dataframeN)
event.set()
dockerLogThread.join()
Visualizziamo i log del docker con il pacchetto 'matplotlib'!
##Visualize Mem & CPU
def visMemCpu(testType,timeSpent,dataframeN,visAll=False):
if visAll==True:
from os import listdir
from os.path import isfile, join
outputFiles = [f for f in listdir("output/") if isfile(join("output/", f))]
combDict = {}
for file in outputFiles:
if "containerStats.json" in file:
with open("output/"+file, "r") as fp:
data = json.load(fp)
fileSplit = file.split("_")
rows = fileSplit[0]
lib = fileSplit[1]
try:
combDict[rows][lib] = {}
combDict[rows][lib]["mem"] = data["mem"]
combDict[rows][lib]["cpu"] = data["cpu"]
combDict[rows][lib]["timeSpent"] = int(data["timeSpent"])
except:
combDict[rows] = {}
combDict[rows][lib] = {}
combDict[rows][lib]["mem"] = data["mem"]
combDict[rows][lib]["cpu"] = data["cpu"]
combDict[rows][lib]["timeSpent"] = int(data["timeSpent"])
with open("output/all.json", "w") as f:
json.dump(combDict, f)
for rows in combDict.keys():
textString = " | "
plt.clf()
for lib in combDict[rows].keys():
plt.plot(combDict[rows][lib]["mem"], label=lib)
textString += lib + " " + str(combDict[rows][lib]["timeSpent"]) + "s | "
plt.xticks([], [])
plt.ylabel("Memory MB")
plt.title(textString)
plt.xlabel("Dataframe rows : " + rows)
plt.legend(framealpha=1, frameon=True)
plt.savefig("output/"+"{}_mem.png".format(str(rows)), dpi=300)
plt.clf()
for lib in combDict[rows].keys():
plt.plot(combDict[rows][lib]["cpu"], label=lib)
plt.xticks([], [])
plt.ylabel("CPU %")
plt.xlabel("Dataframe rows : " + rows)
plt.title(textString)
plt.legend(framealpha=1, frameon=True)
plt.savefig("output/"+"{}_cpu.png".format(str(rows)), dpi=300)
plt.yticks([], [])
25.000 righe
250.000 righe
2.500.000 righe
Conclusione
Questo è tutto! Ora puoi utilizzare Python per controllare le immagini e i contenitori Docker, insieme a un logger di metriche.
Buona programmazione!

![Che cos'è un elenco collegato, comunque? [Parte 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































