[{"data":1,"prerenderedAt":1206},["ShallowReactive",2],{"post-document:\u002Fdevops\u002F2026\u002F08\u002F07\u002FDevops_1\u002F":3},{"id":4,"title":5,"body":6,"categories":1192,"date":1194,"description":1195,"extension":1196,"image":1197,"key_concepts":1197,"last_modified_at":1197,"legacyPath":1198,"meta":1199,"navigation":168,"part":1197,"path":1201,"published":168,"robots":1197,"seo":1202,"series":1197,"stem":1203,"strengths":1197,"summary":1197,"tags":1204,"tradeoffs":1197,"__hash__":1205},"posts\u002Fposts\u002FDevOps\u002F2026-08-07-Devops_1.md","DevOps 기초 1편",{"type":7,"value":8,"toc":1179},"minimark",[9,14,19,23,29,40,47,54,57,63,66,69,85,88,91,95,98,101,107,110,250,253,267,274,287,294,306,311,314,317,334,337,339,343,346,353,356,369,372,393,400,420,423,426,429,438,441,447,450,453,467,470,472,476,479,482,488,491,516,522,525,547,554,556,560,563,566,599,602,627,635,638,658,665,668,671,673,677,680,685,688,694,697,703,706,712,718,721,740,747,750,757,759,761,765,772,775,791,794,811,814,828,835,841,844,846,850,856,865,868,883,886,901,904,910,913,915,919,925,928,935,938,941,947,950,955,1127,1130,1148,1151,1156,1162,1165,1172,1175],[10,11,13],"h2",{"id":12},"devops-기초","DevOps 기초",[15,16,18],"h3",{"id":17},"_1-devops란","1. DevOps란?",[20,21,22],"p",{},"DevOps는 단순히 Docker나 Kubernetes 같은 기술을 사용하는 것을 의미하지 않는다.",[20,24,25],{},[26,27,28],"strong",{},"Development + Operations",[30,31,37],"pre",{"className":32,"code":34,"language":35,"meta":36},[33],"language-text","코드 작성\n   ↓\n테스트\n   ↓\n빌드\n   ↓\n배포\n   ↓\n운영\n   ↓\n모니터링\n   ↓\n문제 수정\n   ↓\n다시 배포\n","text","",[38,39,34],"code",{"__ignoreMap":36},[20,41,42,43,46],{},"이 전체 과정을 ",[26,44,45],{},"빠르고 안정적으로 만드는 개발 및 운영 방식","이라고 볼 수 있다.",[20,48,49,50,53],{},"DevOps의 핵심은 ",[26,51,52],{},"자동화와 재현성","이다.",[20,55,56],{},"예를 들어,",[58,59,60],"blockquote",{},[20,61,62],{},"\"내 컴퓨터에서는 실행되는데 서버에서는 실행되지 않는다.\"",[20,64,65],{},"라는 상황이 반복된다면 안정적인 개발 및 운영 환경이라고 보기 어렵다.",[20,67,68],{},"DevOps에서는 가능한 한",[70,71,72,76,79,82],"ul",{},[73,74,75],"li",{},"같은 실행 환경",[73,77,78],{},"같은 테스트 과정",[73,80,81],{},"같은 배포 방법",[73,83,84],{},"같은 설정 관리 방식",[20,86,87],{},"을 사용하도록 만들어 이러한 문제를 줄인다.",[89,90],"hr",{},[15,92,94],{"id":93},"_2-가장-먼저-알아야-할-것-linux","2. 가장 먼저 알아야 할 것: Linux",[20,96,97],{},"DevOps에서 사용하는 서버 환경은 Linux 기반인 경우가 많다.",[20,99,100],{},"따라서 Docker나 AWS를 배우기 전에 기본적인 Linux 명령어와 터미널 사용법을 익혀두는 것이 좋다.",[20,102,103],{},[104,105,106],"del",{},"참 쉽죠?",[20,108,109],{},"기본적으로 다음 정도는 알아두자.",[30,111,115],{"className":112,"code":113,"language":114,"meta":36,"style":36},"language-bash shiki shiki-themes github-light github-dark","pwd\nls\ncd\nmkdir\ncp\nmv\nrm\n\ncat\nless\ngrep\n\nps\ntop\nkill\n\nchmod\nchown\n\ncurl\n\nssh\n","bash",[38,116,117,126,133,139,145,151,157,163,170,176,182,188,193,199,205,211,216,222,228,233,239,244],{"__ignoreMap":36},[118,119,122],"span",{"class":120,"line":121},"line",1,[118,123,125],{"class":124},"sj4cs","pwd\n",[118,127,129],{"class":120,"line":128},2,[118,130,132],{"class":131},"sScJk","ls\n",[118,134,136],{"class":120,"line":135},3,[118,137,138],{"class":124},"cd\n",[118,140,142],{"class":120,"line":141},4,[118,143,144],{"class":131},"mkdir\n",[118,146,148],{"class":120,"line":147},5,[118,149,150],{"class":131},"cp\n",[118,152,154],{"class":120,"line":153},6,[118,155,156],{"class":131},"mv\n",[118,158,160],{"class":120,"line":159},7,[118,161,162],{"class":131},"rm\n",[118,164,166],{"class":120,"line":165},8,[118,167,169],{"emptyLinePlaceholder":168},true,"\n",[118,171,173],{"class":120,"line":172},9,[118,174,175],{"class":131},"cat\n",[118,177,179],{"class":120,"line":178},10,[118,180,181],{"class":131},"less\n",[118,183,185],{"class":120,"line":184},11,[118,186,187],{"class":131},"grep\n",[118,189,191],{"class":120,"line":190},12,[118,192,169],{"emptyLinePlaceholder":168},[118,194,196],{"class":120,"line":195},13,[118,197,198],{"class":131},"ps\n",[118,200,202],{"class":120,"line":201},14,[118,203,204],{"class":131},"top\n",[118,206,208],{"class":120,"line":207},15,[118,209,210],{"class":124},"kill\n",[118,212,214],{"class":120,"line":213},16,[118,215,169],{"emptyLinePlaceholder":168},[118,217,219],{"class":120,"line":218},17,[118,220,221],{"class":131},"chmod\n",[118,223,225],{"class":120,"line":224},18,[118,226,227],{"class":131},"chown\n",[118,229,231],{"class":120,"line":230},19,[118,232,169],{"emptyLinePlaceholder":168},[118,234,236],{"class":120,"line":235},20,[118,237,238],{"class":131},"curl\n",[118,240,242],{"class":120,"line":241},21,[118,243,169],{"emptyLinePlaceholder":168},[118,245,247],{"class":120,"line":246},22,[118,248,249],{"class":131},"ssh\n",[20,251,252],{},"예를 들어 현재 실행 중인 프로세스를 확인하려면 다음과 같이 사용할 수 있다.",[30,254,256],{"className":112,"code":255,"language":114,"meta":36,"style":36},"ps aux\n",[38,257,258],{"__ignoreMap":36},[118,259,260,263],{"class":120,"line":121},[118,261,262],{"class":131},"ps",[118,264,266],{"class":265},"sZZnC"," aux\n",[20,268,269,270,273],{},"PID가 ",[38,271,272],{},"1234","인 프로세스를 종료하려면 다음과 같이 실행한다.",[30,275,277],{"className":112,"code":276,"language":114,"meta":36,"style":36},"kill 1234\n",[38,278,279],{"__ignoreMap":36},[118,280,281,284],{"class":120,"line":121},[118,282,283],{"class":124},"kill",[118,285,286],{"class":124}," 1234\n",[20,288,289,290,293],{},"서버가 정상적으로 HTTP 응답을 보내는지 확인할 때는 ",[38,291,292],{},"curl","을 사용할 수 있다.",[30,295,297],{"className":112,"code":296,"language":114,"meta":36,"style":36},"curl http:\u002F\u002Flocalhost:8000\n",[38,298,299],{"__ignoreMap":36},[118,300,301,303],{"class":120,"line":121},[118,302,292],{"class":131},[118,304,305],{"class":265}," http:\u002F\u002Flocalhost:8000\n",[307,308,310],"h4",{"id":309},"왜-필요한가","왜 필요한가?",[20,312,313],{},"실제 서버에서 문제가 발생했을 때 GUI 환경이 없는 경우가 많다.",[20,315,316],{},"결국 터미널에서 직접",[70,318,319,322,325,328,331],{},[73,320,321],{},"프로세스가 실행되고 있는가?",[73,323,324],{},"포트가 열려 있는가?",[73,326,327],{},"파일 권한은 정상인가?",[73,329,330],{},"서버가 요청에 응답하는가?",[73,332,333],{},"로그에는 어떤 오류가 기록되었는가?",[20,335,336],{},"등을 확인해야 한다.",[89,338],{},[15,340,342],{"id":341},"_3-shell과-환경변수","3. Shell과 환경변수",[20,344,345],{},"서버에서는 명령어를 반복해서 실행해야 하는 일이 많다.",[20,347,348,349,352],{},"이때 Bash와 같은 ",[26,350,351],{},"Shell","을 사용한다.",[20,354,355],{},"예를 들어 Python 프로그램을 실행하려면 다음과 같이 입력한다.",[30,357,359],{"className":112,"code":358,"language":114,"meta":36,"style":36},"python app.py\n",[38,360,361],{"__ignoreMap":36},[118,362,363,366],{"class":120,"line":121},[118,364,365],{"class":131},"python",[118,367,368],{"class":265}," app.py\n",[20,370,371],{},"환경변수는 다음과 같이 설정할 수 있다.",[30,373,375],{"className":112,"code":374,"language":114,"meta":36,"style":36},"export API_KEY=\"secret\"\n",[38,376,377],{"__ignoreMap":36},[118,378,379,383,387,390],{"class":120,"line":121},[118,380,382],{"class":381},"szBVR","export",[118,384,386],{"class":385},"sVt8B"," API_KEY",[118,388,389],{"class":381},"=",[118,391,392],{"class":265},"\"secret\"\n",[20,394,395,396,399],{},"Python에서는 ",[38,397,398],{},"os.getenv()","를 이용해 환경변수를 가져올 수 있다.",[30,401,404],{"className":402,"code":403,"language":365,"meta":36,"style":36},"language-python shiki shiki-themes github-light github-dark","import os\n\napi_key = os.getenv(\"API_KEY\")\n",[38,405,406,411,415],{"__ignoreMap":36},[118,407,408],{"class":120,"line":121},[118,409,410],{},"import os\n",[118,412,413],{"class":120,"line":128},[118,414,169],{"emptyLinePlaceholder":168},[118,416,417],{"class":120,"line":135},[118,418,419],{},"api_key = os.getenv(\"API_KEY\")\n",[307,421,310],{"id":422},"왜-필요한가-1",[20,424,425],{},"API Key나 DB Password 같은 민감한 정보를 코드에 직접 작성하면 안 된다.",[20,427,428],{},"예를 들어 다음과 같이 작성하는 것은 좋지 않다.",[30,430,432],{"className":402,"code":431,"language":365,"meta":36,"style":36},"API_KEY = \"abcdef123456\"\n",[38,433,434],{"__ignoreMap":36},[118,435,436],{"class":120,"line":121},[118,437,431],{},[20,439,440],{},"대신",[30,442,445],{"className":443,"code":444,"language":35,"meta":36},[33],"코드\n+\n환경변수\n",[38,446,444],{"__ignoreMap":36},[20,448,449],{},"를 분리해서 관리한다.",[20,451,452],{},"이 개념은 이후 배우게 될",[70,454,455,458,461,464],{},[73,456,457],{},"Docker",[73,459,460],{},"GitHub Actions",[73,462,463],{},"AWS",[73,465,466],{},"Kubernetes",[20,468,469],{},"에서도 계속 등장한다.",[89,471],{},[15,473,475],{"id":474},"_4-git과-github","4. Git과 GitHub",[20,477,478],{},"DevOps 자동화는 대부분 Git을 기반으로 시작한다.",[20,480,481],{},"기본적인 흐름은 다음과 같다.",[30,483,486],{"className":484,"code":485,"language":35,"meta":36},[33],"코드 수정\n   ↓\ngit add\n   ↓\ngit commit\n   ↓\ngit push\n   ↓\nGitHub\n",[38,487,485],{"__ignoreMap":36},[20,489,490],{},"필수적으로 알아두어야 할 개념은 다음과 같다.",[70,492,493,496,499,502,505,508,511],{},[73,494,495],{},"Repository",[73,497,498],{},"Commit",[73,500,501],{},"Branch",[73,503,504],{},"Merge",[73,506,507],{},"Pull Request",[73,509,510],{},"Conflict",[73,512,513],{},[38,514,515],{},".gitignore",[20,517,518,519,521],{},"특히 ",[38,520,515],{},"는 중요하다.",[20,523,524],{},"예를 들어 Python 프로젝트에서는 다음과 같은 파일을 Git에서 제외할 수 있다.",[30,526,530],{"className":527,"code":528,"language":529,"meta":36,"style":36},"language-gitignore shiki shiki-themes github-light github-dark",".env\n.venv\u002F\n__pycache__\u002F\n","gitignore",[38,531,532,537,542],{"__ignoreMap":36},[118,533,534],{"class":120,"line":121},[118,535,536],{},".env\n",[118,538,539],{"class":120,"line":128},[118,540,541],{},".venv\u002F\n",[118,543,544],{"class":120,"line":135},[118,545,546],{},"__pycache__\u002F\n",[20,548,549,550,553],{},"API Key가 저장된 ",[38,551,552],{},".env"," 파일이나 로컬 가상환경 같은 파일은 Git 저장소에 올리지 않는 것이 기본이다.",[89,555],{},[15,557,559],{"id":558},"_5-네트워크-기초","5. 네트워크 기초",[20,561,562],{},"DevOps를 배우면서 의외로 자주 막히는 부분이 네트워크다.",[20,564,565],{},"기본적으로 다음 개념은 알아둘 필요가 있다.",[70,567,568,571,574,577,580,583,586,591,596],{},[73,569,570],{},"IP",[73,572,573],{},"Port",[73,575,576],{},"DNS",[73,578,579],{},"HTTP \u002F HTTPS",[73,581,582],{},"TCP",[73,584,585],{},"localhost",[73,587,588],{},[38,589,590],{},"127.0.0.1",[73,592,593],{},[38,594,595],{},"0.0.0.0",[73,597,598],{},"Firewall",[20,600,601],{},"예를 들어 FastAPI 서버를 다음과 같이 실행한다고 하자.",[30,603,605],{"className":112,"code":604,"language":114,"meta":36,"style":36},"uvicorn main:app --host 127.0.0.1 --port 8000\n",[38,606,607],{"__ignoreMap":36},[118,608,609,612,615,618,621,624],{"class":120,"line":121},[118,610,611],{"class":131},"uvicorn",[118,613,614],{"class":265}," main:app",[118,616,617],{"class":124}," --host",[118,619,620],{"class":124}," 127.0.0.1",[118,622,623],{"class":124}," --port",[118,625,626],{"class":124}," 8000\n",[20,628,629,631,632],{},[38,630,590],{},"에 바인딩하면 기본적으로 ",[26,633,634],{},"현재 컴퓨터 내부에서만 접근할 수 있다.",[20,636,637],{},"반면",[30,639,641],{"className":112,"code":640,"language":114,"meta":36,"style":36},"uvicorn main:app --host 0.0.0.0 --port 8000\n",[38,642,643],{"__ignoreMap":36},[118,644,645,647,649,651,654,656],{"class":120,"line":121},[118,646,611],{"class":131},[118,648,614],{"class":265},[118,650,617],{"class":124},[118,652,653],{"class":124}," 0.0.0.0",[118,655,623],{"class":124},[118,657,626],{"class":124},[20,659,660,661,664],{},"으로 실행하면 서버가 ",[26,662,663],{},"모든 네트워크 인터페이스에서 들어오는 요청을 받을 수 있도록 바인딩","된다.",[20,666,667],{},"다만 실제 외부 접근 가능 여부는 방화벽, 공유기, 클라우드 보안 설정 등에 따라서도 달라진다.",[20,669,670],{},"이런 차이를 이해해야 Docker나 Cloud 환경에서 서버를 배포할 때 발생하는 문제를 쉽게 파악할 수 있다.",[89,672],{},[15,674,676],{"id":675},"_6-패키지와-실행-환경-관리","6. 패키지와 실행 환경 관리",[20,678,679],{},"DevOps에서 중요한 원칙 중 하나는 다음과 같다.",[58,681,682],{},[20,683,684],{},"내가 아니더라도 다른 사람이 같은 실행 환경을 재현할 수 있어야 한다.",[20,686,687],{},"개발하면서 자주 발생하는 문제가 있다.",[30,689,692],{"className":690,"code":691,"language":35,"meta":36},[33],"\"내 컴퓨터에서는 되는데?\"\n",[38,693,691],{"__ignoreMap":36},[20,695,696],{},"실제로는 다음과 같은 차이가 원인일 수 있다.",[30,698,701],{"className":699,"code":700,"language":35,"meta":36},[33],"Python 3.11\nPython 3.12\n\nPydantic 1.x\nPydantic 2.x\n\nNumPy 버전 차이\n",[38,702,700],{"__ignoreMap":36},[20,704,705],{},"따라서 프로젝트에서 사용하는 의존성을 기록한다.",[20,707,708,709,53],{},"대표적인 방법이 ",[38,710,711],{},"requirements.txt",[30,713,716],{"className":714,"code":715,"language":35,"meta":36},[33],"fastapi\nuvicorn\npydantic\nhttpx\npytest\nruff\n",[38,717,715],{"__ignoreMap":36},[20,719,720],{},"예를 들어 다음 명령어로 설치할 수 있다.",[30,722,724],{"className":112,"code":723,"language":114,"meta":36,"style":36},"pip install -r requirements.txt\n",[38,725,726],{"__ignoreMap":36},[118,727,728,731,734,737],{"class":120,"line":121},[118,729,730],{"class":131},"pip",[118,732,733],{"class":265}," install",[118,735,736],{"class":124}," -r",[118,738,739],{"class":265}," requirements.txt\n",[20,741,742,743,746],{},"최근 Python 프로젝트에서는 ",[38,744,745],{},"pyproject.toml","을 중심으로 프로젝트 설정과 의존성을 관리하는 방식도 많이 사용한다.",[20,748,749],{},"결국 중요한 것은",[58,751,752],{},[20,753,754],{},[26,755,756],{},"누가 실행하더라도 동일한 환경을 최대한 재현할 수 있도록 만드는 것",[20,758,53],{},[89,760],{},[15,762,764],{"id":763},"_7-코드-품질-검사","7. 코드 품질 검사",[20,766,767,768,771],{},"여기서 앞에서 배운 ",[26,769,770],{},"Ruff","가 연결된다.",[20,773,774],{},"코드 문제를 검사하려면 다음과 같이 실행한다.",[30,776,778],{"className":112,"code":777,"language":114,"meta":36,"style":36},"ruff check .\n",[38,779,780],{"__ignoreMap":36},[118,781,782,785,788],{"class":120,"line":121},[118,783,784],{"class":131},"ruff",[118,786,787],{"class":265}," check",[118,789,790],{"class":265}," .\n",[20,792,793],{},"자동으로 수정 가능한 문제를 고치려면 다음과 같이 사용할 수 있다.",[30,795,797],{"className":112,"code":796,"language":114,"meta":36,"style":36},"ruff check . --fix\n",[38,798,799],{"__ignoreMap":36},[118,800,801,803,805,808],{"class":120,"line":121},[118,802,784],{"class":131},[118,804,787],{"class":265},[118,806,807],{"class":265}," .",[118,809,810],{"class":124}," --fix\n",[20,812,813],{},"코드 포맷을 정리하려면 다음과 같이 실행한다.",[30,815,817],{"className":112,"code":816,"language":114,"meta":36,"style":36},"ruff format .\n",[38,818,819],{"__ignoreMap":36},[118,820,821,823,826],{"class":120,"line":121},[118,822,784],{"class":131},[118,824,825],{"class":265}," format",[118,827,790],{"class":265},[20,829,830,831,834],{},"DevOps에서는 이러한 코드 품질 검사를 사람이 매번 직접 수행하는 것이 아니라 ",[26,832,833],{},"자동화된 파이프라인에 포함시키는 것","이 중요하다.",[30,836,839],{"className":837,"code":838,"language":35,"meta":36},[33],"Developer\n   ↓\nRuff\n   ↓\n문제 없음\n   ↓\n다음 단계\n",[38,840,838],{"__ignoreMap":36},[20,842,843],{},"문제가 발견되면 다음 단계로 넘어가지 않도록 만들 수도 있다.",[89,845],{},[15,847,849],{"id":848},"_8-자동-테스트","8. 자동 테스트",[20,851,852,853,53],{},"그다음 연결되는 것이 ",[38,854,855],{},"pytest",[30,857,859],{"className":112,"code":858,"language":114,"meta":36,"style":36},"pytest\n",[38,860,861],{"__ignoreMap":36},[118,862,863],{"class":120,"line":121},[118,864,858],{"class":131},[20,866,867],{},"예를 들어 다음과 같은 함수가 있다고 하자.",[30,869,871],{"className":402,"code":870,"language":365,"meta":36,"style":36},"def add(a, b):\n    return a + b\n",[38,872,873,878],{"__ignoreMap":36},[118,874,875],{"class":120,"line":121},[118,876,877],{},"def add(a, b):\n",[118,879,880],{"class":120,"line":128},[118,881,882],{},"    return a + b\n",[20,884,885],{},"테스트 코드는 다음과 같이 작성할 수 있다.",[30,887,889],{"className":402,"code":888,"language":365,"meta":36,"style":36},"def test_add():\n    assert add(1, 2) == 3\n",[38,890,891,896],{"__ignoreMap":36},[118,892,893],{"class":120,"line":121},[118,894,895],{},"def test_add():\n",[118,897,898],{"class":120,"line":128},[118,899,900],{},"    assert add(1, 2) == 3\n",[20,902,903],{},"코드를 수정할 때마다 사람이 모든 기능을 직접 확인하는 대신 테스트 프로그램이 코드의 동작을 자동으로 검사한다.",[30,905,908],{"className":906,"code":907,"language":35,"meta":36},[33],"코드 변경\n   ↓\npytest\n   ↓\nPASS \u002F FAIL\n",[38,909,907],{"__ignoreMap":36},[20,911,912],{},"기능이 많아질수록 자동 테스트의 중요성은 더욱 커진다.",[89,914],{},[15,916,918],{"id":917},"_9-ci-continuous-integration","9. CI - Continuous Integration",[20,920,921,922,924],{},"여기서 ",[26,923,460],{},"가 등장한다.",[20,926,927],{},"CI(Continuous Integration)는 간단하게 말하면",[58,929,930],{},[20,931,932],{},[26,933,934],{},"코드가 저장소에 올라왔을 때 자동으로 검사하고 검증하는 과정",[20,936,937],{},"이라고 이해할 수 있다.",[20,939,940],{},"예를 들어 다음과 같은 Pipeline을 구성할 수 있다.",[30,942,945],{"className":943,"code":944,"language":35,"meta":36},[33],"git push\n   ↓\nGitHub Actions\n   ↓\nRuff\n   ↓\npytest\n   ↓\nBuild\n",[38,946,944],{"__ignoreMap":36},[20,948,949],{},"즉 개발자가 코드를 Push하거나 Pull Request를 생성하면 GitHub Actions가 자동으로 코드 품질과 테스트 결과를 검사한다.",[20,951,952],{},[38,953,954],{},".github\u002Fworkflows\u002Fci.yml",[30,956,960],{"className":957,"code":958,"language":959,"meta":36,"style":36},"language-yaml shiki shiki-themes github-light github-dark","name: CI\n\non:\n  push:\n  pull_request:\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions\u002Fcheckout@v4\n\n      - uses: actions\u002Fsetup-python@v5\n        with:\n          python-version: \"3.11\"\n\n      - run: pip install -r requirements.txt\n\n      - run: ruff check .\n\n      - run: pytest\n","yaml",[38,961,962,974,978,986,993,1000,1004,1011,1018,1028,1032,1039,1052,1056,1067,1074,1084,1088,1099,1103,1113,1117],{"__ignoreMap":36},[118,963,964,968,971],{"class":120,"line":121},[118,965,967],{"class":966},"s9eBZ","name",[118,969,970],{"class":385},": ",[118,972,973],{"class":265},"CI\n",[118,975,976],{"class":120,"line":128},[118,977,169],{"emptyLinePlaceholder":168},[118,979,980,983],{"class":120,"line":135},[118,981,982],{"class":124},"on",[118,984,985],{"class":385},":\n",[118,987,988,991],{"class":120,"line":141},[118,989,990],{"class":966},"  push",[118,992,985],{"class":385},[118,994,995,998],{"class":120,"line":147},[118,996,997],{"class":966},"  pull_request",[118,999,985],{"class":385},[118,1001,1002],{"class":120,"line":153},[118,1003,169],{"emptyLinePlaceholder":168},[118,1005,1006,1009],{"class":120,"line":159},[118,1007,1008],{"class":966},"jobs",[118,1010,985],{"class":385},[118,1012,1013,1016],{"class":120,"line":165},[118,1014,1015],{"class":966},"  test",[118,1017,985],{"class":385},[118,1019,1020,1023,1025],{"class":120,"line":172},[118,1021,1022],{"class":966},"    runs-on",[118,1024,970],{"class":385},[118,1026,1027],{"class":265},"ubuntu-latest\n",[118,1029,1030],{"class":120,"line":178},[118,1031,169],{"emptyLinePlaceholder":168},[118,1033,1034,1037],{"class":120,"line":184},[118,1035,1036],{"class":966},"    steps",[118,1038,985],{"class":385},[118,1040,1041,1044,1047,1049],{"class":120,"line":190},[118,1042,1043],{"class":385},"      - ",[118,1045,1046],{"class":966},"uses",[118,1048,970],{"class":385},[118,1050,1051],{"class":265},"actions\u002Fcheckout@v4\n",[118,1053,1054],{"class":120,"line":195},[118,1055,169],{"emptyLinePlaceholder":168},[118,1057,1058,1060,1062,1064],{"class":120,"line":201},[118,1059,1043],{"class":385},[118,1061,1046],{"class":966},[118,1063,970],{"class":385},[118,1065,1066],{"class":265},"actions\u002Fsetup-python@v5\n",[118,1068,1069,1072],{"class":120,"line":207},[118,1070,1071],{"class":966},"        with",[118,1073,985],{"class":385},[118,1075,1076,1079,1081],{"class":120,"line":213},[118,1077,1078],{"class":966},"          python-version",[118,1080,970],{"class":385},[118,1082,1083],{"class":265},"\"3.11\"\n",[118,1085,1086],{"class":120,"line":218},[118,1087,169],{"emptyLinePlaceholder":168},[118,1089,1090,1092,1095,1097],{"class":120,"line":224},[118,1091,1043],{"class":385},[118,1093,1094],{"class":966},"run",[118,1096,970],{"class":385},[118,1098,723],{"class":265},[118,1100,1101],{"class":120,"line":230},[118,1102,169],{"emptyLinePlaceholder":168},[118,1104,1105,1107,1109,1111],{"class":120,"line":235},[118,1106,1043],{"class":385},[118,1108,1094],{"class":966},[118,1110,970],{"class":385},[118,1112,777],{"class":265},[118,1114,1115],{"class":120,"line":241},[118,1116,169],{"emptyLinePlaceholder":168},[118,1118,1119,1121,1123,1125],{"class":120,"line":246},[118,1120,1043],{"class":385},[118,1122,1094],{"class":966},[118,1124,970],{"class":385},[118,1126,858],{"class":265},[20,1128,1129],{},"이렇게 설정해두면 코드가 GitHub에 올라올 때마다 자동으로",[1131,1132,1133,1136,1139,1142,1145],"ol",{},[73,1134,1135],{},"Repository의 코드를 가져오고",[73,1137,1138],{},"Python 3.11 환경을 구성하고",[73,1140,1141],{},"필요한 패키지를 설치하고",[73,1143,1144],{},"Ruff로 코드 품질을 검사하고",[73,1146,1147],{},"pytest로 테스트를 수행한다.",[20,1149,1150],{},"하나라도 실패하면 CI 역시 실패한다.",[20,1152,1153],{},[104,1154,1155],{},"신기한데?",[30,1157,1160],{"className":1158,"code":1159,"language":35,"meta":36},[33],"개발자 코드 작성\n       ↓\n    git push\n       ↓\n GitHub Actions\n       ↓\n ┌───────────────┐\n │               │\nRuff            pytest\n │               │\n └───────┬───────┘\n         ↓\n     PASS \u002F FAIL\n",[38,1161,1159],{"__ignoreMap":36},[20,1163,1164],{},"결국 DevOps의 시작은 Docker, Kubernetes가 아니라,",[58,1166,1167],{},[20,1168,1169],{},[26,1170,1171],{},"코드를 작성하고 → 검사하고 → 테스트하고 → 동일한 방법으로 실행하는 과정을 자동화하는 것",[20,1173,1174],{},"부터 시작한다",[1176,1177,1178],"style",{},"html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":36,"searchDepth":128,"depth":128,"links":1180},[1181],{"id":12,"depth":128,"text":13,"children":1182},[1183,1184,1185,1186,1187,1188,1189,1190,1191],{"id":17,"depth":135,"text":18},{"id":93,"depth":135,"text":94},{"id":341,"depth":135,"text":342},{"id":474,"depth":135,"text":475},{"id":558,"depth":135,"text":559},{"id":675,"depth":135,"text":676},{"id":763,"depth":135,"text":764},{"id":848,"depth":135,"text":849},{"id":917,"depth":135,"text":918},[1193],"DevOps","2026-08-07 09:00:00 +0900","DevOps의 기본 개념과 Linux, 네트워크, Docker, CI\u002FCD로 이어지는 학습 흐름을 정리한다.","md",null,"\u002Fdevops\u002F2026\u002F08\u002F07\u002FDevops_1\u002F",{"layout":1200},"post","\u002Fposts\u002Fdevops\u002F2026-08-07-devops_1",{"title":5,"description":1195},"posts\u002FDevOps\u002F2026-08-07-Devops_1",[1193],"60UD0xkfaKi-cn15HvS7ediHw-6BiHKi2VizdSZHxcc",1788744789344]