Added docker-compose deploy post
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 2m55s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 2m55s
This commit is contained in:
parent
2db3efe80a
commit
62e6776585
747
package-lock.json
generated
747
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -9,10 +9,15 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@types/react-syntax-highlighter": "^15.5.13",
|
||||||
|
"autoprefixer": "^10.4.20",
|
||||||
|
"lucide-react": "^0.477.0",
|
||||||
"next": "15.1.7",
|
"next": "15.1.7",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
|
"react-code-blocks": "^0.1.6",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-icons": "^5.5.0"
|
"react-icons": "^5.5.0",
|
||||||
|
"react-syntax-highlighter": "^15.6.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3",
|
"@eslint/eslintrc": "^3",
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
const config = {
|
const config = {
|
||||||
plugins: {
|
plugins: {
|
||||||
tailwindcss: {},
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,6 +19,8 @@ export default async function BlogPage() {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
posts.reverse();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div style={{paddingTop: "50px"}} className="relative flex justify-center items-center">
|
<div style={{paddingTop: "50px"}} className="relative flex justify-center items-center">
|
||||||
|
2
src/app/blog/posts/1/metadata.ts
Normal file
2
src/app/blog/posts/1/metadata.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export const title = "docker compose deployment";
|
||||||
|
export const description = "Recently I found myself wanting to deploy a docker-compose file to a server. I wanted to automate this process as much as possible, so I decided to write a shell script to do it. This post will go through the process of writing a shell script to deploy a docker-compose file to a server.";
|
60
src/app/blog/posts/1/page.tsx
Normal file
60
src/app/blog/posts/1/page.tsx
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
"use client"
|
||||||
|
import Head from 'next/head';
|
||||||
|
import { title, description } from './metadata';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { IoMdArrowBack } from 'react-icons/io';
|
||||||
|
// import { LuExternalLink } from 'react-icons/lu';
|
||||||
|
|
||||||
|
import { script, basicScript, charm } from "./redeploy"
|
||||||
|
|
||||||
|
import CodeBlock from '../../../../components/code'
|
||||||
|
|
||||||
|
export default function ExamplePost() {
|
||||||
|
return (
|
||||||
|
<div className='pb-[20px]'>
|
||||||
|
<Head>
|
||||||
|
<title>{title}</title>
|
||||||
|
<meta name="description" content={description} />
|
||||||
|
</Head>
|
||||||
|
<div style={{paddingTop: "50px"}} className="relative flex justify-center items-center">
|
||||||
|
<div className="absolute left-0 ml-6">
|
||||||
|
<Link href={"/blog"}>
|
||||||
|
<IoMdArrowBack size={30}/>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
<span className="text-xl">{title}</span>
|
||||||
|
</div>
|
||||||
|
<div style={{paddingTop: "30px"}} className='flex justify-center items-center md:w-[70%] xl:w-[50%] w-[85%] mx-auto'>
|
||||||
|
<div>
|
||||||
|
<p>{description}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style={{paddingTop: "30px"}} className='flex flex-col justify-center items-center md:w-[70%] xl:w-[50%] w-[85%] mx-auto'>
|
||||||
|
<p>TLDR, if you just want to check out the script and how to use it, you can find it <Link className='text-blue-500' href={"https://git.asherfalcon.com/asher/compose-deploy"} target="_blank" rel="noopener noreferrer">here</Link></p>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<p>As I started deploying more and more services to my server using docker compose, I kept just copying the whole file, sshing into the server, deleting the file and creating a new one, pasting the content. As you can imagine, that got old fast. Realising I was losing an extra minute or two every time I wanted to re-deploy my <b>docker-compose.yml</b> file, I created a simple git repository, and cloned it on both hosts. This meant I could simply push on my laptop and ssh into the server and run git pull, and I would never have to mess around with copying the whole file manually again. </p>
|
||||||
|
<br/>
|
||||||
|
<p>Now I was still having to ssh in and stop all the containers, pull the repo and then start them again, and I though it would be nice if I could just do this from the terminal in the VSCode editor I used to edit the docker-compose.yml file, so I wrote a simple script which does all of it for me.</p>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<CodeBlock code={basicScript} language="bash" />
|
||||||
|
<div style={{paddingTop: "30px"}} className='flex flex-col justify-center items-center md:w-[70%] xl:w-[50%] w-[85%] mx-auto'>
|
||||||
|
<p>So what's going on here? Firstly we commit and push all the changed files to the repository, this means that the docker-compose.yml and any other files, for example config files for services you may be running, are now on the remote repository.</p>
|
||||||
|
<br/>
|
||||||
|
<p>Next, it simply connects to the server via ssh, and runs the commands listed. Firstly, stopping the containers so that the docker-compose.yml and any other config files can be updated safely, then pulling the git repo to update the files. Finally restarting the docker compose services, and then exiting so the ssh session closes.</p>
|
||||||
|
<br />
|
||||||
|
<p>Now to make the commit history not look like groundhog day, and to add some commit messages, we can use <Link className='text-blue-500' href="https://github.com/charmbracelet/gum" target="_blank" rel="noopener noreferrer">Gum</Link>, a tool for easily making interactive bash scripts. It works by installing the application onto your system which can then be called inside your bash scripts to invoke user prompts, for example we can use the code below to get a commit message from the user in the terminal and store it as a variable.</p>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
<CodeBlock code={charm} language="bash" />
|
||||||
|
<div style={{paddingTop: "30px"}} className='flex flex-col justify-center items-center md:w-[70%] xl:w-[50%] w-[85%] mx-auto'>
|
||||||
|
<p>Finally, putting all the pieces together and adding a quick check to see if gum is installed, and trying to install it if not, we can arrive at the following script:</p>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
<CodeBlock code={script} language="bash" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
87
src/app/blog/posts/1/redeploy.ts
Normal file
87
src/app/blog/posts/1/redeploy.ts
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
export const charm = `#!/bin/bash
|
||||||
|
message=$(gum input --placeholder "Commit message")
|
||||||
|
git add .
|
||||||
|
git commit -m "\${message}"
|
||||||
|
`
|
||||||
|
|
||||||
|
export const basicScript = `#!/bin/bash
|
||||||
|
git add .
|
||||||
|
git commit -m "updated docker-compose.yml"
|
||||||
|
git push
|
||||||
|
|
||||||
|
ssh -o "StrictHostKeyChecking=no" -i ssh.key $CD_USER@$CD_HOST << EOF
|
||||||
|
sudo su
|
||||||
|
cd $CD_PATH
|
||||||
|
docker compose down
|
||||||
|
git pull
|
||||||
|
docker compose up -d
|
||||||
|
exit
|
||||||
|
EOF
|
||||||
|
`
|
||||||
|
|
||||||
|
export const script = `#!/bin/bash
|
||||||
|
|
||||||
|
# A script to redeploy this docker infrastructure using ssh
|
||||||
|
|
||||||
|
if [ -f .env ]; then
|
||||||
|
source .env
|
||||||
|
else
|
||||||
|
echo ".env file not found. Please create it and set the necessary environment variables."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
check_gum() {
|
||||||
|
if command -v gum &> /dev/null; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo "Gum not found, installing..."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
install_gum() {
|
||||||
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
|
/bin/bash -c "$(curl -fsSL
|
||||||
|
https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||||
|
brew install gum
|
||||||
|
elif [[ -f /etc/os-release ]]; then
|
||||||
|
source /etc/os-release
|
||||||
|
if [[ $ID == "ubuntu" ]] || [[ $ID == "debian" ]]; then
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y gum
|
||||||
|
elif [[ $ID == "fedora" ]] || [[ $ID == "centos" ]] || [[ $ID == "rhel" ]]; then
|
||||||
|
sudo dnf install -y gum
|
||||||
|
else
|
||||||
|
echo "Unsupported Linux distribution. Please install Gum
|
||||||
|
manually."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Unsupported operating system. Please install Gum manually."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main script execution
|
||||||
|
if ! check_gum; then
|
||||||
|
if ! install_gum; then
|
||||||
|
echo "Failed to install Gum. Please install it manually and run
|
||||||
|
the script again."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
message=$(gum input --placeholder "Commit message")
|
||||||
|
|
||||||
|
git add .
|
||||||
|
git commit -m "\${message}"
|
||||||
|
git push
|
||||||
|
|
||||||
|
ssh -o "StrictHostKeyChecking=no" -i ssh.key $CD_USER@$CD_HOST << EOF
|
||||||
|
sudo su
|
||||||
|
cd $CD_PATH
|
||||||
|
docker compose down
|
||||||
|
git pull
|
||||||
|
docker compose up -d
|
||||||
|
exit
|
||||||
|
EOF`
|
@ -24,9 +24,9 @@ export default function RootLayout({
|
|||||||
<Head>
|
<Head>
|
||||||
<meta name="viewport" content="viewport-fit=cover" />
|
<meta name="viewport" content="viewport-fit=cover" />
|
||||||
</Head>
|
</Head>
|
||||||
<script defer src="https://cloud.umami.is/script.js" data-website-id="eaac838f-0bef-4455-a834-2e1455d78d8c"></script>
|
|
||||||
<body className={`bg-ashwhite ${open_sans.className} `} style={{height: '100vh'}}>
|
<body className={`bg-ashwhite ${open_sans.className} `} style={{height: '100vh'}}>
|
||||||
{children}
|
{children}
|
||||||
|
<script defer src="https://cloud.umami.is/script.js" data-website-id="eaac838f-0bef-4455-a834-2e1455d78d8c"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
47
src/components/code.tsx
Normal file
47
src/components/code.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { Copy, Check } from "lucide-react";
|
||||||
|
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||||
|
|
||||||
|
interface CodeBlockProps {
|
||||||
|
code: string;
|
||||||
|
language?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CodeBlock({ code, language = "typescript" }: CodeBlockProps) {
|
||||||
|
const [copied, setCopied] = useState(false);
|
||||||
|
|
||||||
|
const handleCopy = async () => {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(code);
|
||||||
|
setCopied(true);
|
||||||
|
setTimeout(() => setCopied(false), 1500);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Failed to copy:", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="md:w-[70%] xl:w-[50%] w-[85%] mx-auto">
|
||||||
|
<div className="relative text-white">
|
||||||
|
<div style={{position: "absolute", right: "10px", top: "10px"}}>
|
||||||
|
<button
|
||||||
|
onClick={handleCopy}
|
||||||
|
className="p-2 hover:bg-gray-700 rounded"
|
||||||
|
>
|
||||||
|
{copied ? <Check size={16} color="#000000" /> :
|
||||||
|
<Copy size={16} color="#000000" />
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<center>
|
||||||
|
<SyntaxHighlighter
|
||||||
|
language={language}
|
||||||
|
customStyle={{ backgroundColor: "transparent", border: "2px solid #000000", borderRadius: "8px", padding: "1rem", width: "100%", textAlign:"left"}}
|
||||||
|
>
|
||||||
|
{code}
|
||||||
|
</SyntaxHighlighter>
|
||||||
|
</center>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -2,9 +2,9 @@ import type { Config } from "tailwindcss";
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
content: [
|
content: [
|
||||||
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
|
"./src/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
|
"./src/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
|
"./src/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
],
|
],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user