|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8" /> |
|
<title>Concept to Code Millionaire</title> |
|
<meta name="viewport" content="width=device-width, initial-scale=1" /> |
|
<script src="https://cdn.tailwindcss.com"></script> |
|
</head> |
|
<body class="bg-gray-100 min-h-screen flex items-center justify-center p-6"> |
|
<div class="bg-white p-8 rounded-2xl shadow-xl w-full max-w-2xl"> |
|
<h1 class="text-4xl font-bold text-indigo-600 text-center mb-2">Concept to Code Millionaire</h1> |
|
<p class="text-center text-sm text-gray-500 mb-6"> |
|
Source: |
|
<a class="underline hover:text-indigo-700" target="_blank" |
|
href="https://www.linkedin.com/pulse/docker-from-concept-commands-michael-lively-mndwe/"> |
|
Docker: From Concept to Commands — Michael Lively (Aug 23, 2025) |
|
</a> |
|
</p> |
|
|
|
<div id="board"> |
|
<div id="error" class="hidden mb-3 text-sm text-red-600 bg-red-50 border border-red-200 rounded p-2"></div> |
|
<div id="question" class="text-2xl font-semibold text-gray-800 mb-4"></div> |
|
<div id="choices" class="space-y-3"></div> |
|
|
|
<div id="lifelines" class="flex justify-around mt-6"> |
|
<button id="life5050" class="lifeline bg-yellow-400 hover:bg-yellow-500 text-white font-medium py-2 px-4 rounded-lg shadow">50:50</button> |
|
<button id="lifePoll" class="lifeline bg-blue-400 hover:bg-blue-500 text-white font-medium py-2 px-4 rounded-lg shadow">Ask the Class</button> |
|
<button id="lifeHint" class="lifeline bg-green-400 hover:bg-green-500 text-white font-medium py-2 px-4 rounded-lg shadow">Ask an Expert</button> |
|
</div> |
|
|
|
<div id="poll" class="text-sm text-gray-700 mt-4"></div> |
|
<div id="hint" class="text-sm italic text-gray-600 mt-2"></div> |
|
|
|
<button id="next" class="hidden mt-8 bg-indigo-600 hover:bg-indigo-700 text-white font-semibold py-2 px-6 rounded-lg shadow"> |
|
Next Question |
|
</button> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
window.addEventListener('DOMContentLoaded', () => { |
|
const showError = (msg) => { |
|
const el = document.getElementById('error'); |
|
el.textContent = msg; |
|
el.classList.remove('hidden'); |
|
}; |
|
|
|
try { |
|
const questions = [ |
|
{ q: "What core problem does Docker reduce by packaging apps with all dependencies?", |
|
choices: ["\"My CPU is too slow\" problem","\"It works on my machine\" problem","Vendor lock-in of cloud credits","I/O bottlenecks from HDDs"], |
|
correct: 1, hint: "Called out explicitly as a dreaded phrase." }, |
|
{ q: "Which command verifies a fresh Docker install with a tiny test image?", |
|
choices: ["docker info","docker run hello-world","docker version","docker verify install"], |
|
correct: 1, hint: "It both pulls and runs a simple image." }, |
|
{ q: "Best description of image vs. container?", |
|
choices: ["Image is the running process; container is the template","Image is a blueprint; container is a running instance","Both are the same","Container builds images automatically"], |
|
correct: 1, hint: "Blueprint vs. instance." }, |
|
{ q: "Which pairing lists Docker’s foundational components?", |
|
choices: ["Daemon (dockerd) and Client (CLI)","Supervisor and Operator","Scheduler and Controller","Planner and Runner"], |
|
correct: 0, hint: "One manages objects; one is your command tool." }, |
|
{ q: "On Windows/macOS, which bundle includes daemon, CLI, and tools?", |
|
choices: ["Docker Desktop","Docker Studio","Docker Workstation","Docker Hub Client"], |
|
correct: 0, hint: "Standard local install for devs." }, |
|
{ q: "Most well-known public image registry?", |
|
choices: ["Artifact Registry","Docker Hub","Harbor","Quay"], |
|
correct: 1, hint: "Default source for many pulls." }, |
|
{ q: "Which pair runs Nginx and maps 80→8080 on host?", |
|
choices: ["docker pull nginx && docker run -d -p 8080:80 nginx","docker build nginx && docker start -p 8080:80 nginx","docker fetch nginx && docker exec -p 8080:80 nginx","docker clone nginx && docker up -p 8080:80"], |
|
correct: 0, hint: "Pull, then run detached with -p host:container." }, |
|
{ q: "Which file defines how to build your image?", |
|
choices: ["compose.yaml","Dockerfile","package.json","Container.toml"], |
|
correct: 1, hint: "Plain text build instructions." }, |
|
{ q: "Which build/run pair tags and launches on port 5000?", |
|
choices: ["docker build -t myapp:1.0 . && docker run -d -p 5000:5000 myapp:1.0","docker build myapp . && docker start -p 5000:5000 myapp","docker compile -t myapp . && docker exec -p 5000 myapp","docker make -t myapp . && docker deploy -p 5000 myapp"], |
|
correct: 0, hint: "Tag with -t, run with -p host:container." }, |
|
{ q: "Day-to-day container lifecycle commands?", |
|
choices: ["docker ps, docker stop, docker rm, docker logs","docker get, docker halt, docker purge, docker read","docker view, docker end, docker delete, docker cat","docker list, docker kill, docker trash, docker echo"], |
|
correct: 0, hint: "Canonical verbs in the article." }, |
|
{ q: "Keep images small and efficient by…", |
|
choices: ["Installing all dev tools in final image","Using multi-stage builds and small bases (e.g., alpine)","Bundling multiple apps per container","Avoiding .dockerignore"], |
|
correct: 1, hint: "Less is more." }, |
|
{ q: "Reclaim disk by removing unused images:", |
|
choices: ["docker image prune -a","docker images --rm all","docker clean images","docker rmi --unused"], |
|
correct: 0, hint: "Prune is the key." }, |
|
{ q: "Simplest built-in clustering for small projects:", |
|
choices: ["Enable Swarm with docker swarm init","Install Kubernetes the hard way","Write a custom scheduler","Use a systemd unit file"], |
|
correct: 0, hint: "Built into Docker." }, |
|
{ q: "Swarm vs. Kubernetes — best summary:", |
|
choices: ["Swarm is richer for enterprises","Kubernetes is the de facto standard for large-scale orchestration","Equal production adoption","Swarm has the larger ecosystem"], |
|
correct: 1, hint: "Think ecosystem and adoption." }, |
|
{ q: "Kubernetes quick start sequence from the article:", |
|
choices: ["minikube start → kubectl create deployment nginx --image=nginx → kubectl expose deployment nginx --port=80 --type=NodePort", |
|
"kubeadm up → kctl make deploy nginx → kctl service open 80", |
|
"kubestart → kubectl add nginx → kubectl publish 80", |
|
"kind boot → kubectl run nginx --image=nginx → kubectl open service 80"], |
|
correct: 0, hint: "Minikube, create deployment, expose NodePort." } |
|
]; |
|
|
|
|
|
questions.forEach((q, i) => { |
|
const target = i % 4; |
|
if (q.correct !== target) { |
|
[q.choices[target], q.choices[q.correct]] = [q.choices[q.correct], q.choices[target]]; |
|
q.correct = target; |
|
} |
|
}); |
|
|
|
const prizes = [100,200,300,500,1000,2000,4000,8000,16000,32000,64000,125000,250000,500000,1000000]; |
|
let idx = 0; |
|
let currentPrize = prizes[0]; |
|
|
|
const qEl = document.getElementById('question'); |
|
const cEl = document.getElementById('choices'); |
|
const pollEl = document.getElementById('poll'); |
|
const hintEl = document.getElementById('hint'); |
|
const nextBtn = document.getElementById('next'); |
|
const boardEl = document.getElementById('board'); |
|
|
|
function updateQuestionDisplay() { |
|
qEl.textContent = `$${currentPrize}: ${questions[idx].q}`; |
|
} |
|
|
|
function showQuestion() { |
|
|
|
['life5050','lifePoll','lifeHint'].forEach(id => { |
|
const btn = document.getElementById(id); |
|
btn.disabled = false; |
|
btn.classList.remove('opacity-50'); |
|
}); |
|
document.getElementById('error').classList.add('hidden'); |
|
pollEl.textContent = ''; |
|
hintEl.textContent = ''; |
|
nextBtn.classList.add('hidden'); |
|
|
|
currentPrize = prizes[idx]; |
|
updateQuestionDisplay(); |
|
|
|
cEl.innerHTML = questions[idx].choices.map((ch,i) => |
|
`<button class="choice bg-indigo-500 hover:bg-indigo-600 text-white font-medium py-2 px-4 rounded w-full" data-i="${i}"> |
|
${String.fromCharCode(65+i)}. ${ch} |
|
</button>` |
|
).join(''); |
|
|
|
document.querySelectorAll('.choice').forEach(btn => { |
|
btn.disabled = false; |
|
btn.onclick = selectAnswer; |
|
}); |
|
} |
|
|
|
function selectAnswer(e) { |
|
const chosen = +e.currentTarget.dataset.i; |
|
const corr = questions[idx].correct; |
|
document.querySelectorAll('.choice').forEach(b => b.disabled = true); |
|
|
|
if (chosen === corr) { |
|
e.currentTarget.classList.replace('bg-indigo-500','bg-green-500'); |
|
} else { |
|
e.currentTarget.classList.replace('bg-indigo-500','bg-red-500'); |
|
const correctBtn = document.querySelector(`.choice[data-i="${corr}"]`); |
|
if (correctBtn) correctBtn.classList.replace('bg-indigo-500','bg-green-500'); |
|
} |
|
nextBtn.classList.remove('hidden'); |
|
} |
|
|
|
|
|
document.getElementById('life5050').onclick = () => { |
|
const btn = document.getElementById('life5050'); |
|
if (btn.disabled) return; |
|
btn.disabled = true; btn.classList.add('opacity-50'); |
|
|
|
const corr = questions[idx].correct; |
|
[0,1,2,3].filter(i => i !== corr).sort(() => Math.random()-0.5).slice(0,2).forEach(i => { |
|
const b = document.querySelector(`.choice[data-i="${i}"]`); |
|
if (b) { b.disabled = true; b.classList.add('opacity-50'); } |
|
}); |
|
|
|
currentPrize = Math.floor(currentPrize / 2); |
|
updateQuestionDisplay(); |
|
}; |
|
|
|
document.getElementById('lifePoll').onclick = () => { |
|
const btn = document.getElementById('lifePoll'); |
|
if (btn.disabled) return; |
|
btn.disabled = true; btn.classList.add('opacity-50'); |
|
|
|
const corr = questions[idx].correct; |
|
const pct = [0,0,0,0]; |
|
pct[corr] = 70 + Math.floor(Math.random() * 21); |
|
let rem = 100 - pct[corr]; |
|
[0,1,2,3].filter(i => i !== corr).forEach((i,j,arr) => { |
|
const last = j === arr.length - 1; |
|
const alloc = last ? rem : Math.floor(rem / (arr.length - j)); |
|
pct[i] = alloc; rem -= alloc; |
|
}); |
|
pollEl.textContent = `Class Poll: A:${pct[0]}% B:${pct[1]}% C:${pct[2]}% D:${pct[3]}%`; |
|
}; |
|
|
|
document.getElementById('lifeHint').onclick = () => { |
|
const btn = document.getElementById('lifeHint'); |
|
if (btn.disabled) return; |
|
btn.disabled = true; btn.classList.add('opacity-50'); |
|
hintEl.textContent = `Expert Hint: ${questions[idx].hint}`; |
|
}; |
|
|
|
nextBtn.onclick = () => { |
|
idx++; |
|
if (idx < questions.length) { |
|
showQuestion(); |
|
} else { |
|
boardEl.innerHTML = '<div class="text-3xl font-bold text-green-600 text-center">🎉 Congratulations! You’ve mastered Concept to Code Millionaire! 🎉</div>'; |
|
} |
|
}; |
|
|
|
|
|
showQuestion(); |
|
|
|
} catch (err) { |
|
console.error(err); |
|
showError("JavaScript error prevented the questions from rendering. Open the browser console for details."); |
|
} |
|
}); |
|
</script> |
|
</body> |
|
</html> |
|
|