Project

General

Profile

Actions

새기능 #1

open

Docker DevOps Development and Environment Settings

Added by Eungsu Kweon about 2 months ago.

Status:
신규
Priority:
보통
Assignee:
Target version:
-
Start date:
10/07/2025
Due date:
% Done:

0%

Estimated time:

Description

{{html

<title>Docker 기반 개발 환경 아키텍처 가이드</title> <script src="https://cdn.tailwindcss.com"></script> <style> body { font-family: 'Pretendard', sans-serif; background-color: #f8fafc; color: #1e293b; } @import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.css'); .tab-btn.active { border-color: #3b82f6; color: #3b82f6; font-weight: 600; } .step { transition: all 0.3s ease-in-out; } .step.active { transform: scale(1.05); box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); border-color: #3b82f6; } .arrow { transition: all 0.3s ease-in-out; } .arrow.active .arrow-path { stroke: #3b82f6; stroke-width: 2.5px; } .arrow.active .arrow-head { fill: #3b82f6; } .fade-in { animation: fadeIn 0.5s ease-in-out; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } </style>

Docker 기반 DevOps 솔루션

<main class="container mx-auto px-6 py-12">
    <section id="architecture" class="mb-24 text-center">
        <h2 class="text-4xl font-bold mb-4">전체 아키텍처</h2>
        <p class="text-lg text-slate-500 max-w-3xl mx-auto mb-12">
            Docker를 중심으로 Git, Private Registry, CI/CD, 관리 도구를 통합하여 효율적인 개발 및 배포 환경을 구축합니다. Nginx Proxy Manager를 통해 모든 서비스에 안전하게 접근할 수 있습니다.
        </p>
        <div class="bg-white p-8 rounded-xl shadow-lg border border-slate-200">
            <div class="relative max-w-5xl mx-auto">
                <div class="grid grid-cols-1 md:grid-cols-3 gap-8 items-center">
                    <div class="bg-blue-50 border-2 border-dashed border-blue-200 p-6 rounded-lg">
                        <h3 class="font-bold text-blue-800 text-lg">Developer</h3>
                        <div class="text-blue-600 mt-2">Code & Dockerfile</div>
                    </div>
                    <div class="hidden md:block text-slate-400">
                        <svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 mx-auto" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3" /></svg>
                        <span class="text-sm">Git Push</span>
                    </div>
                    <div class="bg-green-50 border-2 border-dashed border-green-200 p-6 rounded-lg">
                        <h3 class="font-bold text-green-800 text-lg">DevOps Platform</h3>
                        <div class="text-green-600 mt-2">Gitea, Registry, Redmine, Portainer...</div>
                    </div>
                </div>
                <div class="my-8 flex justify-center items-center text-slate-400">
                     <svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7l4-4m0 0l4 4m-4-4v18" /></svg>
                     <svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 17l-4 4m0 0l-4-4m4 4V3" /></svg>
                     <span class="text-sm mx-4 font-semibold">Managed By</span>
                </div>
                <div class="bg-purple-50 border-2 border-dashed border-purple-200 p-6 rounded-lg max-w-md mx-auto">
                    <h3 class="font-bold text-purple-800 text-lg">Nginx Proxy Manager</h3>
                    <div class="text-purple-600 mt-2">Single Entry Point & SSL</div>
                </div>
            </div>
        </div>
    </section>

    <section id="components" class="mb-24">
        <h2 class="text-4xl font-bold mb-4 text-center">핵심 구성요소</h2>
        <p class="text-lg text-slate-500 max-w-3xl mx-auto mb-12 text-center">
            각 솔루션은 특정 역할을 수행하며, 이들이 유기적으로 연동되어 강력한 DevOps 파이프라인을 형성합니다.
        </p>
        <div class="bg-white p-8 rounded-xl shadow-lg border border-slate-200">
            <div class="flex border-b border-slate-200 mb-6">
                <button data-tab="gitea" class="tab-btn p-4 border-b-2 border-transparent text-slate-500 active">Gitea</button>
                <button data-tab="registry" class="tab-btn p-4 border-b-2 border-transparent text-slate-500">Docker Registry</button>
                <button data-tab="redmine" class="tab-btn p-4 border-b-2 border-transparent text-slate-500">Redmine</button>
                <button data-tab="portainer" class="tab-btn p-4 border-b-2 border-transparent text-slate-500">Portainer</button>
                <button data-tab="npm" class="tab-btn p-4 border-b-2 border-transparent text-slate-500">Nginx Proxy Manager</button>
            </div>
            <div id="tab-content" class="min-h-[200px]">
                <div data-content="gitea" class="fade-in">
                    <h3 class="text-2xl font-bold text-slate-800 mb-2">Gitea (Git 서버)</h3>
                    <p class="text-slate-600">경량 Git 서버로, 소스 코드와 Dockerfile 등 모든 형상 자산을 중앙에서 관리합니다. 팀원 간의 코드 공유, 버전 관리, 협업의 중심 역할을 합니다. GitLab이나 GitHub와 유사한 기능을 제공하지만 훨씬 가볍고 설치가 간편합니다.</p>
                </div>
                <div data-content="registry" class="hidden fade-in">
                    <h3 class="text-2xl font-bold text-slate-800 mb-2">Private Docker Registry</h3>
                    <p class="text-slate-600">빌드된 Docker 이미지를 안전하게 저장하고 관리하는 사설 저장소입니다. 외부 Docker Hub에 의존하지 않고, 내부 네트워크에서 빠르고 안정적으로 이미지를 배포할 수 있습니다. 보안이 중요한 환경에 필수적입니다.</p>
                </div>
                <div data-content="redmine" class="hidden fade-in">
                    <h3 class="text-2xl font-bold text-slate-800 mb-2">Redmine (프로젝트 관리)</h3>
                    <p class="text-slate-600">이슈 추적, 프로젝트 관리, 위키 등 다양한 기능을 제공하는 웹 기반 도구입니다. 개발 작업의 진행 상황을 추적하고, 버그를 관리하며, 프로젝트 관련 문서를 중앙에서 관리할 수 있어 팀의 생산성을 높여줍니다.</p>
                </div>
                <div data-content="portainer" class="hidden fade-in">
                    <h3 class="text-2xl font-bold text-slate-800 mb-2">Portainer (Docker 관리 UI)</h3>
                    <p class="text-slate-600">Docker 환경을 위한 강력한 웹 UI를 제공합니다. 컨테이너, 이미지, 볼륨, 네트워크 등 Docker의 모든 요소를 그래픽 인터페이스를 통해 쉽게 관리하고 모니터링할 수 있어 복잡한 Docker 명령어를 대체할 수 있습니다.</p>
                </div>
                <div data-content="npm" class="hidden fade-in">
                    <h3 class="text-2xl font-bold text-slate-800 mb-2">Nginx Proxy Manager</h3>
                    <p class="text-slate-600">여러 내부 서비스(Gitea, Redmine 등)를 외부 도메인과 연결해주는 리버스 프록시입니다. Let's Encrypt를 통한 무료 SSL 인증서 발급 및 갱신을 자동화하여 모든 서비스를 HTTPS로 안전하게 노출시킬 수 있습니다.</p>
                </div>
            </div>
        </div>
    </section>

    <section id="workflow" class="mb-24">
        <h2 class="text-4xl font-bold mb-4 text-center">개발 및 배포 워크플로우</h2>
        <p class="text-lg text-slate-500 max-w-3xl mx-auto mb-12 text-center">
            개발자의 코드 커밋부터 Docker 이미지 빌드, 레지스트리 저장, 그리고 배포까지의 전체 흐름을 단계별로 확인해보세요.
        </p>
        <div class="bg-white p-8 rounded-xl shadow-lg border border-slate-200">
            <div id="workflow-diagram" class="grid grid-cols-1 md:grid-cols-5 items-center gap-x-4 gap-y-8 text-center">
                <div class="step p-4 border-2 rounded-lg" data-step="0">
                    <div class="text-3xl mb-2">👨‍💻</div>
                    <h4 class="font-bold">1. 코드 작성 및 수정</h4>
                    <p class="text-sm text-slate-500">Developer</p>
                </div>
                <div class="arrow mx-auto" data-step="0">
                    <svg class="w-12 h-12 text-slate-300 -rotate-90 md:rotate-0" viewBox="0 0 24 24"><path class="arrow-path" fill="none" stroke="currentColor" stroke-width="2" d="M5 12h14"></path><path class="arrow-head" fill="currentColor" d="M16 7l5 5-5 5z"></path></svg>
                </div>
                <div class="step p-4 border-2 rounded-lg" data-step="1">
                    <div class="text-3xl mb-2">📂</div>
                    <h4 class="font-bold">2. Git Push</h4>
                    <p class="text-sm text-slate-500">Gitea Server</p>
                </div>
                <div class="arrow mx-auto" data-step="1">
                   <svg class="w-12 h-12 text-slate-300 -rotate-90 md:rotate-0" viewBox="0 0 24 24"><path class="arrow-path" fill="none" stroke="currentColor" stroke-width="2" d="M5 12h14"></path><path class="arrow-head" fill="currentColor" d="M16 7l5 5-5 5z"></path></svg>
                </div>
                <div class="step p-4 border-2 rounded-lg" data-step="2">
                    <div class="text-3xl mb-2">🏗️</div>
                    <h4 class="font-bold">3. Docker 이미지 빌드</h4>
                    <p class="text-sm text-slate-500">CI/CD or Manual</p>
                </div>
                <div class="hidden md:grid col-span-5 grid-cols-5 items-center">
                  <div class="col-start-5 arrow mx-auto" data-step="2">
                       <svg class="w-12 h-12 text-slate-300 -rotate-90" viewBox="0 0 24 24"><path class="arrow-path" fill="none" stroke="currentColor" stroke-width="2" d="M5 12h14"></path><path class="arrow-head" fill="currentColor" d="M16 7l5 5-5 5z"></path></svg>
                  </div>
                </div>
                <div class="step p-4 border-2 rounded-lg" data-step="4">
                    <div class="text-3xl mb-2">🚀</div>
                    <h4 class="font-bold">5. 이미지 배포/관리</h4>
                    <p class="text-sm text-slate-500">Portainer</p>
                </div>
                <div class="arrow mx-auto" data-step="3">
                    <svg class="w-12 h-12 text-slate-300 rotate-180 -rotate-90 md:rotate-180" viewBox="0 0 24 24"><path class="arrow-path" fill="none" stroke="currentColor" stroke-width="2" d="M5 12h14"></path><path class="arrow-head" fill="currentColor" d="M16 7l5 5-5 5z"></path></svg>
                </div>
                <div class="step p-4 border-2 rounded-lg" data-step="3">
                    <div class="text-3xl mb-2">📦</div>
                    <h4 class="font-bold">4. 레지스트리 Push</h4>
                    <p class="text-sm text-slate-500">Private Registry</p>
                </div>
                <div class="hidden md:block"></div>
                <div class="hidden md:block"></div>
            </div>

            <div class="mt-8 bg-slate-50 p-6 rounded-lg min-h-[100px] text-center">
                <p id="workflow-description" class="text-slate-700 fade-in"></p>
            </div>
            <div class="mt-6 flex justify-center space-x-4">
                <button id="prev-step" class="bg-white hover:bg-slate-100 text-slate-700 font-bold py-2 px-4 rounded-lg border border-slate-300 transition-colors">이전 단계</button>
                <button id="next-step" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-lg transition-colors">다음 단계</button>
            </div>
        </div>
    </section>

    <section id="setup">
        <h2 class="text-4xl font-bold mb-4 text-center">초기 설정 가이드</h2>
        <p class="text-lg text-slate-500 max-w-3xl mx-auto mb-12 text-center">
            Docker Compose를 사용하여 전체 솔루션을 구성하는 방법입니다. 아래 `docker-compose.yml` 파일을 기반으로 각 서비스의 설정을 진행합니다.
        </p>
        <div class="bg-white p-8 rounded-xl shadow-lg border border-slate-200">
            <div class="space-y-4">
                <div class="border-b pb-4">
                    <h3 class="text-xl font-semibold">1. 사전 준비</h3>
                    <p class="mt-2 text-slate-600">서버에 Docker와 Docker Compose가 설치되어 있어야 합니다. 또한, 각 서비스에 사용할 도메인(예: `git.yourdomain.com`, `registry.yourdomain.com`)을 준비하고 DNS 설정을 완료해야 합니다.</p>
                </div>
                <div class="border-b pb-4">
                    <h3 class="text-xl font-semibold">2. 디렉토리 구조 생성</h3>
                    <p class="mt-2 text-slate-600">설정 파일과 데이터를 영구적으로 보관하기 위해 아래와 같은 디렉토리 구조를 생성합니다. 이 구조는 `docker-compose.yml` 파일의 `volumes` 설정과 일치해야 합니다.</p>
                    <pre class="bg-slate-800 text-white p-4 rounded-md mt-4 text-sm overflow-x-auto"><code>/opt/devops/

├── docker-compose.yml
├── gitea/
├── registry/
├── redmine/
├── portainer/
└── npm/


3. docker-compose.yml 작성


아래 예시 코드를 docker-compose.yml 파일에 붙여넣고, 자신의 환경에 맞게 volumes 경로, 포트, 도메인 등의 변수를 수정하세요.



Copy
version: '3.8'

services:
gitea:
image: gitea/gitea:latest
container_name: gitea
environment:
- USER_UID=1000
- USER_GID=1000
restart: always
networks:
- devops_net
volumes:
- /opt/devops/gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000"
- "2222:22"

registry:
image: registry:2
container_name: registry
restart: always
networks:
- devops_net
volumes:
- /opt/devops/registry:/var/lib/registry

redmine:
image: redmine:latest
container_name: redmine
restart: always
networks:
- devops_net
environment:
- REDMINE_DB_POSTGRES=db
- REDMINE_DB_USERNAME=redmine
- REDMINE_DB_PASSWORD=your_secure_password
volumes:
- /opt/devops/redmine/files:/usr/src/redmine/files
- /opt/devops/redmine/plugins:/usr/src/redmine/plugins
depends_on:
- db

db:
image: postgres:13
container_name: redmine_db
restart: always
networks:
- devops_net
environment:
- POSTGRES_USER=redmine
- POSTGRES_PASSWORD=your_secure_password
- POSTGRES_DB=redmine
volumes:
- /opt/devops/redmine/postgres:/var/lib/postgresql/data

portainer:
image: portainer/portainer-ce:latest
container_name: portainer
command: -H unix:///var/run/docker.sock
restart: always
networks:
- devops_net
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /opt/devops/portainer:/data

npm:
image: 'jc21/nginx-proxy-manager:latest'
container_name: npm
restart: always
ports:
- '80:80'
- '81:81'
- '443:443'
networks:
- devops_net
volumes:
- /opt/devops/npm/data:/data
- /opt/devops/npm/letsencrypt:/etc/letsencrypt

networks:
devops_net:
driver: bridge





4. 서비스 실행 및 설정


터미널에서 docker-compose.yml 파일이 있는 디렉토리로 이동한 후, docker-compose up -d 명령어를 실행하여 모든 컨테이너를 백그라운드에서 실행합니다.



  • Nginx Proxy Manager: http://YOUR_SERVER_IP:81 로 접속하여 초기 설정을 완료하고, 각 서비스(Gitea, Redmine 등)를 도메인과 연결하고 SSL 인증서를 발급합니다.

  • Gitea: http://git.yourdomain.com (프록시 설정 후)으로 접속하여 관리자 계정을 생성하고 레포지토리를 설정합니다.

  • Redmine, Portainer: 각각 설정된 도메인으로 접속하여 초기 설정을 진행합니다.






<footer class="bg-slate-800 text-slate-400 mt-16">
    <div class="container mx-auto px-6 py-8 text-center">
        <p>&copy; 2025 Interactive DevOps Guide. All rights reserved.</p>
    </div>
</footer>

<script>
    document.addEventListener('DOMContentLoaded', () => {
        const tabs = document.querySelectorAll('.tab-btn');
        const tabContents = document.querySelectorAll('[data-content]');
        const tabContainer = document.querySelector('.flex.border-b');

        tabContainer.addEventListener('click', e => {
            if (e.target.matches('.tab-btn')) {
                tabs.forEach(tab => tab.classList.remove('active'));
                e.target.classList.add('active');

                const targetContent = e.target.dataset.tab;
                tabContents.forEach(content => {
                    if (content.dataset.content === targetContent) {
                        content.classList.remove('hidden');
                    } else {
                        content.classList.add('hidden');
                    }
                });
            }
        });
        
        let currentStep = 0;
        const steps = document.querySelectorAll('.step');
        const arrows = document.querySelectorAll('.arrow');
        const description = document.getElementById('workflow-description');
        const nextBtn = document.getElementById('next-step');
        const prevBtn = document.getElementById('prev-step');
        const totalSteps = steps.length;

        const descriptions = [
            "개발자는 새로운 기능을 추가하거나 버그를 수정합니다. 이 과정에서 애플리케이션 소스 코드와 함께 배포에 필요한 Dockerfile을 작성하거나 업데이트합니다.",
            "작업이 완료된 코드는 버전 관리를 위해 Gitea 서버로 Push됩니다. 이를 통해 변경 이력이 기록되고 팀원들과 코드를 공유할 수 있습니다.",
            "Gitea에 Push된 최신 코드를 기반으로 Docker 이미지를 빌드합니다. 이 과정은 Jenkins 같은 CI/CD 도구를 통해 자동화하거나, 개발자가 직접 수동으로 실행할 수 있습니다.",
            "성공적으로 빌드된 Docker 이미지는 버전 태그와 함께 사설 Docker Registry에 Push되어 저장됩니다. 이제 이 이미지는 언제든지 배포에 사용될 수 있습니다.",
            "Portainer 또는 kubectl/docker-compose 명령어를 사용하여 레지스트리에 저장된 최신 이미지를 가져와 서버에 배포(실행)합니다. 이로써 사용자에게 새로운 버전의 서비스가 제공됩니다."
        ];

        function updateWorkflowView() {
            steps.forEach((step, index) => {
                if (index === currentStep) {
                    step.classList.add('active');
                } else {
                    step.classList.remove('active');
                }
            });
            
            arrows.forEach((arrow, index) => {
                if (index === currentStep || (currentStep === totalSteps - 1 && index === totalSteps - 2)) {
                    arrow.classList.add('active');
                } else {
                    arrow.classList.remove('active');
                }
            });
            
            description.textContent = descriptions[currentStep];
            description.classList.remove('fade-in');
            void description.offsetWidth;
            description.classList.add('fade-in');

            prevBtn.disabled = currentStep === 0;
            prevBtn.classList.toggle('opacity-50', currentStep === 0);
            nextBtn.disabled = currentStep === totalSteps - 1;
            nextBtn.classList.toggle('opacity-50', currentStep === totalSteps - 1);
        }

        nextBtn.addEventListener('click', () => {
            if (currentStep < totalSteps - 1) {
                currentStep++;
                updateWorkflowView();
            }
        });

        prevBtn.addEventListener('click', () => {
            if (currentStep > 0) {
                currentStep--;
                updateWorkflowView();
            }
        });
        
        updateWorkflowView();
    });

    function copyToClipboard(elementId) {
        const codeEl = document.getElementById(elementId);
        const text = codeEl.textContent;
        
        const tempTextArea = document.createElement('textarea');
        tempTextArea.value = text;
        document.body.appendChild(tempTextArea);
        tempTextArea.select();
        
        try {
            document.execCommand('copy');
            alert('코드가 클립보드에 복사되었습니다.');
        } catch (err) {
            console.error('클립보드 복사 실패:', err);
            alert('복사에 실패했습니다.');
        }
        
        document.body.removeChild(tempTextArea);
    }

</script>
}}

No data to display

Actions

Also available in: Atom PDF