Kubernetes์—์„œ ๋ฐฐํฌ๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ  ์ž๋™ํ™”ํ•˜๊ธฐ ์œ„ํ•œ ์ƒˆ๋กœ์šด ๋„๊ตฌ ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ

Kubernetes์—์„œ ๋ฐฐํฌ๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ  ์ž๋™ํ™”ํ•˜๊ธฐ ์œ„ํ•œ ์ƒˆ๋กœ์šด ๋„๊ตฌ ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ

์•ˆ๋…•ํ•˜์„ธ์š”! ์ตœ๊ทผ Docker ์ด๋ฏธ์ง€ ๊ตฌ์ถ•๊ณผ Kubernetes ๋ฐฐํฌ๋ฅผ ์œ„ํ•œ ๋ฉ‹์ง„ ์ž๋™ํ™” ๋„๊ตฌ๊ฐ€ ๋งŽ์ด ์ถœ์‹œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ์ ์—์„œ ๋‚˜๋Š” GitLab์„ ์‹œํ—˜ํ•ด๋ณด๊ณ  ๊ทธ ๊ธฐ๋Šฅ์„ ์ฒ ์ €ํ•˜๊ฒŒ ์—ฐ๊ตฌํ•˜๊ณ  ๋ฌผ๋ก  ํŒŒ์ดํ”„๋ผ์ธ์„ ์„ค์ •ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ณธ ์ž‘ํ’ˆ์€ ํ™ˆํŽ˜์ด์ง€์—์„œ ์˜๊ฐ์„ ๋ฐ›์•„ ์ œ์ž‘๋˜์—ˆ์Šต๋‹ˆ๋‹ค ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค.io, ์ด๋Š” ๋‹ค์Œ์—์„œ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ์†Œ์Šค ์ฝ”๋“œ ์ž๋™์œผ๋กœ ์ „์†ก๋œ ๊ฐ ํ’€ ์š”์ฒญ์— ๋Œ€ํ•ด ๋กœ๋ด‡์€ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ํฌํ•จ๋œ ์‚ฌ์ดํŠธ์˜ ๋ฏธ๋ฆฌ ๋ณด๊ธฐ ๋ฒ„์ „์„ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋Š” ๋งํฌ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์œ ์‚ฌํ•œ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ฒ˜์Œ๋ถ€ํ„ฐ ๊ตฌ์ถ•ํ•˜๋ ค๊ณ  ์‹œ๋„ํ–ˆ์ง€๋งŒ Kubernetes์— ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ฐฐํฌํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ–ˆ๋˜ Gitlab CI์™€ ๋ฌด๋ฃŒ ๋„๊ตฌ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์™„์ „ํžˆ ๊ตฌ์ถ•๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์˜ค๋Š˜์€ ๋งˆ์นจ๋‚ด ๊ทธ๋“ค์— ๋Œ€ํ•ด ๋” ์ž์„ธํžˆ ์•Œ๋ ค ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

์ด ๊ธฐ์‚ฌ์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋„๊ตฌ์— ๋Œ€ํ•ด ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.
ํœด๊ณ , ํ๋ฒก, ์นด๋‹ˆ์ฝ”, ์ž์‹ ์•”ํ˜ธ ะธ ๊นƒ๋žฉ CI ์—ญ๋™์ ์ธ ํ™˜๊ฒฝ์„ ์กฐ์„ฑํ•ฉ๋‹ˆ๋‹ค.

๋‚ด์šฉ๋ฌผ

  1. ํœด๊ณ ๋ฅผ ๋งŒ๋‚˜๋ณด์„ธ์š”
  2. Dockerfile ์ค€๋น„
  3. ์นด๋‹ˆ์ฝ”๋ฅผ ์•Œ์•„๊ฐ€๋Š” ์ค‘
  4. qbec์— ๋Œ€ํ•ด ์•Œ์•„๊ฐ€๊ธฐ
  5. Kubernetes-executor๋กœ Gitlab-runner ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ
  6. qbec์„ ์‚ฌ์šฉํ•˜์—ฌ Helm ์ฐจํŠธ ๋ฐฐํฌ
  7. git-crypt ์†Œ๊ฐœ
  8. ๋„๊ตฌ ์ƒ์ž ์ด๋ฏธ์ง€ ๋งŒ๋“ค๊ธฐ
  9. ์ฒซ ๋ฒˆ์งธ ํŒŒ์ดํ”„๋ผ์ธ๊ณผ ํƒœ๊ทธ๋ณ„ ์ด๋ฏธ์ง€ ์กฐ๋ฆฝ
  10. ๋ฐฐํฌ ์ž๋™ํ™”
  11. ๋งˆ์Šคํ„ฐ๋กœ ํ‘ธ์‹œํ•  ๋•Œ ์•„ํ‹ฐํŒฉํŠธ ๋ฐ ์–ด์…ˆ๋ธ”๋ฆฌ
  12. ๋™์  ํ™˜๊ฒฝ
  13. ์•ฑ ๊ฒ€ํ† 

1. ํœด๊ณ ์— ๋Œ€ํ•ด ์•Œ์•„๊ฐ€๊ธฐ

์šฐ๋ฆฌ ํ”„๋กœ์ ํŠธ์˜ ์˜ˆ๋กœ Hugo๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌ์ถ•๋œ ๋ฌธ์„œ ๊ฒŒ์‹œ ์‚ฌ์ดํŠธ๋ฅผ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. Hugo๋Š” ์ •์  ์ฝ˜ํ…์ธ  ์ƒ์„ฑ๊ธฐ์ž…๋‹ˆ๋‹ค.

์ •์  ์ƒ์„ฑ๊ธฐ์— ๋Œ€ํ•ด ์ž˜ ๋ชจ๋ฅด์‹œ๋Š” ๋ถ„๋“ค์„ ์œ„ํ•ด ์กฐ๊ธˆ ๋” ์„ค๋ช…๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์š”์ฒญํ•  ๋•Œ ์ฆ‰์‹œ ํŽ˜์ด์ง€๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์ผ๋ถ€ PHP๋ฅผ ๊ฐ–์ถ˜ ๊ธฐ์กด ์›น ์‚ฌ์ดํŠธ ์—”์ง„๊ณผ ๋‹ฌ๋ฆฌ ์ •์  ์ƒ์„ฑ๊ธฐ๋Š” ์•ฝ๊ฐ„ ๋‹ค๋ฅด๊ฒŒ ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ผ๋ฐ˜์ ์œผ๋กœ Markdown ๋งˆํฌ์—… ๋ฐ ํ…Œ๋งˆ ํ…œํ”Œ๋ฆฟ์˜ ํŒŒ์ผ ์„ธํŠธ์ธ ์†Œ์Šค๋ฅผ ๊ฐ€์ ธ์˜จ ๋‹ค์Œ ์™„์ „ํžˆ ์™„์„ฑ๋œ ์›น ์‚ฌ์ดํŠธ๋กœ ์ปดํŒŒ์ผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฆ‰, ๊ฒฐ๊ณผ์ ์œผ๋กœ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ์™€ ์ƒ์„ฑ๋œ HTML ํŒŒ์ผ ์„ธํŠธ๋ฅผ ๋ฐ›๊ฒŒ ๋˜๋ฉฐ, ์ด๋ฅผ ์ €๋ ดํ•œ ํ˜ธ์ŠคํŒ…์— ์—…๋กœ๋“œํ•˜๊ณ  ์ž‘๋™ํ•˜๋Š” ์›น์‚ฌ์ดํŠธ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Hugo๋ฅผ ๋กœ์ปฌ์— ์„ค์น˜ํ•˜๊ณ  ์‚ฌ์šฉํ•ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ƒˆ ์‚ฌ์ดํŠธ ์ดˆ๊ธฐํ™”:

hugo new site docs.example.org

๋™์‹œ์— git ์ €์žฅ์†Œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

cd docs.example.org
git init

์ง€๊ธˆ๊นŒ์ง€ ์šฐ๋ฆฌ ์‚ฌ์ดํŠธ๋Š” ๊นจ๋—ํ–ˆ์œผ๋ฉฐ ์—ฌ๊ธฐ์— ๋ญ”๊ฐ€๋ฅผ ํ‘œ์‹œํ•˜๋ ค๋ฉด ๋จผ์ € ํ…Œ๋งˆ๋ฅผ ์—ฐ๊ฒฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ…Œ๋งˆ๋Š” ์‚ฌ์ดํŠธ๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ํ…œํ”Œ๋ฆฟ๊ณผ ์ง€์ •๋œ ๊ทœ์น™์˜ ์ง‘ํ•ฉ์ผ ๋ฟ์ž…๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•  ํ…Œ๋งˆ๋Š” ์ž๋ฃŒ์‹ค, ์ œ ์ƒ๊ฐ์—๋Š” ๋ฌธ์„œ ์‚ฌ์ดํŠธ์— ์™„๋ฒฝํ•˜๊ฒŒ ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

ํ…Œ๋งˆ ํŒŒ์ผ์„ ํ”„๋กœ์ ํŠธ ์ €์žฅ์†Œ์— ์ €์žฅํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๋Š” ์‚ฌ์‹ค์— ํŠน๋ณ„ํ•œ ์ฃผ์˜๋ฅผ ๊ธฐ์šธ์ด๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  ๋‹ค์Œ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ„๋‹จํžˆ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์‹ ํ•˜์œ„ ๋ชจ๋“ˆ:

git submodule add https://github.com/matcornic/hugo-theme-learn themes/learn

๋”ฐ๋ผ์„œ ์ €์žฅ์†Œ์—๋Š” ํ”„๋กœ์ ํŠธ์™€ ์ง์ ‘ ๊ด€๋ จ๋œ ํŒŒ์ผ๋งŒ ํฌํ•จ๋˜๋ฉฐ ์—ฐ๊ฒฐ๋œ ํ…Œ๋งˆ๋Š” ํŠน์ • ์ €์žฅ์†Œ์— ๋Œ€ํ•œ ๋งํฌ์™€ ์ปค๋ฐ‹์œผ๋กœ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค. ์ฆ‰, ํ•ญ์ƒ ์›๋ณธ ์†Œ์Šค์—์„œ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์œผ๋ฉฐ ๊ฑฑ์ •ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ํ˜ธํ™˜๋˜์ง€ ์•Š๋Š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ.

์„ค์ •์„ ์ˆ˜์ •ํ•ด๋ณด์ž config.toml:

baseURL = "http://docs.example.org/"
languageCode = "en-us"
title = "My Docs Site"
theme = "learn"

์ด๋ฏธ ์ด ๋‹จ๊ณ„์—์„œ ๋‹ค์Œ์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

hugo server

๊ทธ๋ฆฌ๊ณ  ์ฃผ์†Œ์—๋Š” http://localhost:1313/ ์ƒˆ๋กœ ์ƒ์„ฑ๋œ ์›น์‚ฌ์ดํŠธ๋ฅผ ํ™•์ธํ•˜์„ธ์š”. ๋””๋ ‰ํ† ๋ฆฌ์˜ ๋ชจ๋“  ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ ๋ธŒ๋ผ์šฐ์ €์— ์—ด๋ ค ์žˆ๋Š” ํŽ˜์ด์ง€๋ฅผ ์ž๋™์œผ๋กœ ์—…๋ฐ์ดํŠธํ•˜๋ฏ€๋กœ ๋งค์šฐ ํŽธ๋ฆฌํ•ฉ๋‹ˆ๋‹ค!

ํ‘œ์ง€๋ฅผ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ฝ˜ํ…์ธ /_index.md:

# My docs site

## Welcome to the docs!

You will be very smart :-)

์ƒˆ๋กœ ์ƒ์„ฑ๋œ ํŽ˜์ด์ง€์˜ ์Šคํฌ๋ฆฐ์ƒท

Kubernetes์—์„œ ๋ฐฐํฌ๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ  ์ž๋™ํ™”ํ•˜๊ธฐ ์œ„ํ•œ ์ƒˆ๋กœ์šด ๋„๊ตฌ ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ

์‚ฌ์ดํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋ ค๋ฉด ๋‹ค์Œ์„ ์‹คํ–‰ํ•˜์„ธ์š”.

hugo

๋””๋ ‰ํ† ๋ฆฌ ๋‚ด์šฉ ๊ณต๊ณต์˜/ ๊ทธ๋ฆฌ๊ณ  ๋‹น์‹ ์˜ ์›น์‚ฌ์ดํŠธ๊ฐ€ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.
๋„ค, ๊ทธ๋Ÿฐ๋ฐ ์ฆ‰์‹œ ์ถ”๊ฐ€ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. .gitignore:

echo /public > .gitignore

๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ปค๋ฐ‹ํ•˜๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋งˆ์„ธ์š”.

git add .
git commit -m "New site created"

2. Dockerfile ์ค€๋น„

์ด์ œ ์ €์žฅ์†Œ์˜ ๊ตฌ์กฐ๋ฅผ ์ •์˜ํ•  ์ฐจ๋ก€์ž…๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋ณดํ†ต ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒƒ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

.
โ”œโ”€โ”€ deploy
โ”‚   โ”œโ”€โ”€ app1
โ”‚   โ””โ”€โ”€ app2
โ””โ”€โ”€ dockerfiles
    โ”œโ”€โ”€ image1
    โ””โ”€โ”€ image2

  • ๋„์ปคํŒŒ์ผ/ โ€” Dockerfiles๊ฐ€ ์žˆ๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ์™€ Docker ์ด๋ฏธ์ง€๋ฅผ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ๋ชจ๋“  ๊ฒƒ์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ฐฐํฌ/ โ€” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ Kubernetes์— ๋ฐฐํฌํ•˜๊ธฐ ์œ„ํ•œ ๋””๋ ‰ํ„ฐ๋ฆฌ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ๊ฒฝ๋กœ๋ฅผ ๋”ฐ๋ผ ์ฒซ ๋ฒˆ์งธ Dockerfile์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. dockerfiles/์›น์‚ฌ์ดํŠธ/Dockerfile

FROM alpine:3.11 as builder
ARG HUGO_VERSION=0.62.0
RUN wget -O- https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_linux-64bit.tar.gz | tar -xz -C /usr/local/bin
ADD . /src
RUN hugo -s /src

FROM alpine:3.11
RUN apk add --no-cache darkhttpd
COPY --from=builder /src/public /var/www
ENTRYPOINT [ "/usr/bin/darkhttpd" ]
CMD [ "/var/www" ]

๋ณด์‹œ๋‹ค์‹œํ”ผ Dockerfile์—๋Š” ๋‘ ๊ฐ€์ง€๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. FROM, ์ด๋ฒˆ ๊ธฐํšŒ๋ฅผ ๋‹ค๋‹จ๊ณ„ ๋นŒ๋“œ ์ตœ์ข… Docker ์ด๋ฏธ์ง€์—์„œ ๋ถˆํ•„์š”ํ•œ ๋ชจ๋“  ๊ฒƒ์„ ์ œ์™ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๋”ฐ๋ผ์„œ ์ตœ์ข… ์ด๋ฏธ์ง€์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‚ด์šฉ๋งŒ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. darkhttpd (๊ฒฝ๋Ÿ‰ HTTP ์„œ๋ฒ„) ๋ฐ ๊ณต๊ณต์˜/ โ€” ์ •์ ์œผ๋กœ ์ƒ์„ฑ๋œ ์›น์‚ฌ์ดํŠธ์˜ ์ฝ˜ํ…์ธ .

๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ปค๋ฐ‹ํ•˜๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋งˆ์„ธ์š”.

git add dockerfiles/website
git commit -m "Add Dockerfile for website"

3. ์นด๋‹ˆ์ฝ”๋ฅผ ์•Œ์•„๊ฐ€๋‹ค

๋„์ปค ์ด๋ฏธ์ง€ ๋นŒ๋”๋กœ์„œ ์ €๋Š” ๋‹ค์Œ์„ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ์นด๋‹ˆ์ฝ”, ์ž‘์—…์—๋Š” ๋„์ปค ๋ฐ๋ชฌ์ด ํ•„์š”ํ•˜์ง€ ์•Š๊ณ  ๋นŒ๋“œ ์ž์ฒด๋Š” ๋ชจ๋“  ์‹œ์Šคํ…œ์—์„œ ์ˆ˜ํ–‰๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์บ์‹œ๋Š” ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ์ง์ ‘ ์ €์žฅ๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋ณธ๊ฒฉ์ ์ธ ์˜๊ตฌ ์ €์žฅ์†Œ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•˜๋ ค๋ฉด ๋‹ค์Œ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‹คํ–‰ํ•˜์„ธ์š”. ์นด๋‹ˆ์ฝ” ์ง‘ํ–‰์ž ํ˜„์žฌ ๋นŒ๋“œ ์ปจํ…์ŠคํŠธ๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” docker๋ฅผ ํ†ตํ•ด ๋กœ์ปฌ๋กœ ์ˆ˜ํ–‰ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

docker run -ti --rm 
  -v $PWD:/workspace 
  -v ~/.docker/config.json:/kaniko/.docker/config.json:ro 
  gcr.io/kaniko-project/executor:v0.15.0 
  --cache 
  --dockerfile=dockerfiles/website/Dockerfile 
  --destination=registry.gitlab.com/kvaps/docs.example.org/website:v0.0.1

ะ“ะดะต Registry.gitlab.com/kvaps/docs.example.org/website โ€” ๋„์ปค ์ด๋ฏธ์ง€์˜ ์ด๋ฆ„, ๋นŒ๋“œ ํ›„ ๋„์ปค ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์—์„œ ์ž๋™์œผ๋กœ ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค.

๋งค๊ฐœ ๋ณ€์ˆ˜ --์€๋‹‰์ฒ˜ docker ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์—์„œ ๋ ˆ์ด์–ด๋ฅผ ์บ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฃผ์–ด์ง„ ์˜ˆ์—์„œ๋Š” ํ•ด๋‹น ๋ ˆ์ด์–ด๊ฐ€ ๋‹ค์Œ ์œ„์น˜์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. Registry.gitlab.com/kvaps/docs.example.org/website/cacheํ•˜์ง€๋งŒ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค๋ฅธ ๊ฒฝ๋กœ๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. --์บ์‹œ ์ €์žฅ์†Œ.

docker-registry ์Šคํฌ๋ฆฐ์ƒท

Kubernetes์—์„œ ๋ฐฐํฌ๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ  ์ž๋™ํ™”ํ•˜๊ธฐ ์œ„ํ•œ ์ƒˆ๋กœ์šด ๋„๊ตฌ ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ

4. qbec์— ๋Œ€ํ•ด ์•Œ์•„๊ฐ€๊ธฐ

ํ€˜๋ฒก ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋งค๋‹ˆํŽ˜์ŠคํŠธ๋ฅผ ์„ ์–ธ์ ์œผ๋กœ ์„ค๋ช…ํ•˜๊ณ  ์ด๋ฅผ Kubernetes์— ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฐํฌ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. Jsonnet์„ ๊ธฐ๋ณธ ๊ตฌ๋ฌธ์œผ๋กœ ์‚ฌ์šฉํ•˜๋ฉด ์—ฌ๋Ÿฌ ํ™˜๊ฒฝ ๊ฐ„์˜ ์ฐจ์ด์ ์— ๋Œ€ํ•œ ์„ค๋ช…์„ ํฌ๊ฒŒ ๋‹จ์ˆœํ™”ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์ฝ”๋“œ ๋ฐ˜๋ณต์„ ๊ฑฐ์˜ ์™„์ „ํžˆ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—ฌ๋Ÿฌ ํด๋Ÿฌ์Šคํ„ฐ์— ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ฐฐํฌํ•ด์•ผ ํ•˜๊ณ  ์ด๋ฅผ Git์—์„œ ์„ ์–ธ์ ์œผ๋กœ ์„ค๋ช…ํ•˜๋ ค๋Š” ๊ฒฝ์šฐ์— ํŠนํžˆ ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค.

Qbec์„ ์‚ฌ์šฉํ•˜๋ฉด ํ•„์š”ํ•œ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ „๋‹ฌํ•˜์—ฌ Helm ์ฐจํŠธ๋ฅผ ๋ Œ๋”๋งํ•œ ๋‹ค์Œ ๋‹ค์–‘ํ•œ ๋ณ€ํ˜•์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์„ ํฌํ•จํ•˜์—ฌ ์ผ๋ฐ˜ ๋งค๋‹ˆํŽ˜์ŠคํŠธ์™€ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ChartMuseum์„ ์‚ฌ์šฉํ•˜์„ธ์š”. ์ฆ‰, ์ฐจํŠธ๊ฐ€ ์†ํ•œ Git์—์„œ ์ง์ ‘ ์ฐจํŠธ๋ฅผ ์ €์žฅํ•˜๊ณ  ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์•ž์„œ ๋งํ–ˆ๋“ฏ์ด ์šฐ๋ฆฌ๋Š” ๋ชจ๋“  ๋ฐฐํฌ๋ฅผ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ๋ฐฐํฌ/:

mkdir deploy
cd deploy

์ฒซ ๋ฒˆ์งธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ดˆ๊ธฐํ™”ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

qbec init website
cd website

์ด์ œ ์šฐ๋ฆฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

.
โ”œโ”€โ”€ components
โ”œโ”€โ”€ environments
โ”‚   โ”œโ”€โ”€ base.libsonnet
โ”‚   โ””โ”€โ”€ default.libsonnet
โ”œโ”€โ”€ params.libsonnet
โ””โ”€โ”€ qbec.yaml

ํŒŒ์ผ์„ ์‚ดํŽด๋ณด์ž qbec.yaml:

apiVersion: qbec.io/v1alpha1
kind: App
metadata:
  name: website
spec:
  environments:
    default:
      defaultNamespace: docs
      server: https://kubernetes.example.org:8443
  vars: {}

์—ฌ๊ธฐ์„œ ์šฐ๋ฆฌ๋Š” ์ฃผ๋กœ ๋‹ค์Œ ์‚ฌํ•ญ์— ๊ด€์‹ฌ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์–‘ ํ™˜๊ฒฝ, qbec์€ ์ด๋ฏธ ์šฐ๋ฆฌ๋ฅผ ์œ„ํ•œ ๊ธฐ๋ณธ ํ™˜๊ฒฝ์„ ์ƒ์„ฑํ–ˆ์œผ๋ฉฐ ํ˜„์žฌ kubeconfig์—์„œ ์„œ๋ฒ„ ์ฃผ์†Œ์™€ ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋ฅผ ๊ฐ€์ ธ์™”์Šต๋‹ˆ๋‹ค.
์ด์ œ ๋ฐฐํฌํ•  ๋•Œ ๋””ํดํŠธ ๊ฐ’ ํ™˜๊ฒฝ์—์„œ qbec์€ ํ•ญ์ƒ ์ง€์ •๋œ Kubernetes ํด๋Ÿฌ์Šคํ„ฐ์™€ ์ง€์ •๋œ ๋„ค์ž„์ŠคํŽ˜์ด์Šค์—๋งŒ ๋ฐฐํฌ๋ฉ๋‹ˆ๋‹ค. ์ฆ‰, ๋ฐฐํฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ๋” ์ด์ƒ ์ปจํ…์ŠคํŠธ์™€ ๋„ค์ž„์ŠคํŽ˜์ด์Šค ์‚ฌ์ด๋ฅผ ์ „ํ™˜ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.
ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์–ธ์ œ๋“ ์ง€ ์ด ํŒŒ์ผ์˜ ์„ค์ •์„ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ชจ๋“  ํ™˜๊ฒฝ์€ ๋‹ค์Œ์— ์„ค๋ช…๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. qbec.yaml, ๊ทธ๋ฆฌ๊ณ  ํŒŒ์ผ์—์„œ params.libsonnet, ์—ฌ๊ธฐ์—๋Š” ํ•ด๋‹น ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋Š” ์œ„์น˜๊ฐ€ ๋‚˜์™€ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์œผ๋กœ ๋‘ ๊ฐœ์˜ ๋””๋ ‰ํ„ฐ๋ฆฌ๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

  • ๊ตฌ์„ฑ ์š”์†Œ / โ€” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ชจ๋“  ๋งค๋‹ˆํŽ˜์ŠคํŠธ๊ฐ€ ์—ฌ๊ธฐ์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. jsonnet ๋ฐ ์ผ๋ฐ˜ yaml ํŒŒ์ผ ๋ชจ๋‘์—์„œ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ™˜๊ฒฝ/ โ€” ์—ฌ๊ธฐ์„œ๋Š” ํ™˜๊ฒฝ์— ๋Œ€ํ•œ ๋ชจ๋“  ๋ณ€์ˆ˜(๋งค๊ฐœ๋ณ€์ˆ˜)๋ฅผ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ์ ์œผ๋กœ ๋‘ ๊ฐœ์˜ ํŒŒ์ผ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ํ™˜๊ฒฝ/base.libsonnet - ๋ชจ๋“  ํ™˜๊ฒฝ์— ๋Œ€ํ•œ ๊ณตํ†ต ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.
  • ํ™˜๊ฒฝ/default.libsonnet โ€” ํ™˜๊ฒฝ์— ๋Œ€ํ•ด ์žฌ์ •์˜๋œ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ๋””ํดํŠธ ๊ฐ’

์—ด์–ด๋ณด์ž ํ™˜๊ฒฝ/base.libsonnet ๊ฑฐ๊ธฐ์— ์ฒซ ๋ฒˆ์งธ ๊ตฌ์„ฑ์š”์†Œ์— ๋Œ€ํ•œ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

{
  components: {
    website: {
      name: 'example-docs',
      image: 'registry.gitlab.com/kvaps/docs.example.org/website:v0.0.1',
      replicas: 1,
      containerPort: 80,
      servicePort: 80,
      nodeSelector: {},
      tolerations: [],
      ingressClass: 'nginx',
      domain: 'docs.example.org',
    },
  },
}

์ฒซ ๋ฒˆ์งธ ๊ตฌ์„ฑ ์š”์†Œ๋„ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ตฌ์„ฑ ์š”์†Œ/website.jsonnet:

local env = {
  name: std.extVar('qbec.io/env'),
  namespace: std.extVar('qbec.io/defaultNs'),
};
local p = import '../params.libsonnet';
local params = p.components.website;

[
  {
    apiVersion: 'apps/v1',
    kind: 'Deployment',
    metadata: {
      labels: { app: params.name },
      name: params.name,
    },
    spec: {
      replicas: params.replicas,
      selector: {
        matchLabels: {
          app: params.name,
        },
      },
      template: {
        metadata: {
          labels: { app: params.name },
        },
        spec: {
          containers: [
            {
              name: 'darkhttpd',
              image: params.image,
              ports: [
                {
                  containerPort: params.containerPort,
                },
              ],
            },
          ],
          nodeSelector: params.nodeSelector,
          tolerations: params.tolerations,
          imagePullSecrets: [{ name: 'regsecret' }],
        },
      },
    },
  },
  {
    apiVersion: 'v1',
    kind: 'Service',
    metadata: {
      labels: { app: params.name },
      name: params.name,
    },
    spec: {
      selector: {
        app: params.name,
      },
      ports: [
        {
          port: params.servicePort,
          targetPort: params.containerPort,
        },
      ],
    },
  },
  {
    apiVersion: 'extensions/v1beta1',
    kind: 'Ingress',
    metadata: {
      annotations: {
        'kubernetes.io/ingress.class': params.ingressClass,
      },
      labels: { app: params.name },
      name: params.name,
    },
    spec: {
      rules: [
        {
          host: params.domain,
          http: {
            paths: [
              {
                backend: {
                  serviceName: params.name,
                  servicePort: params.servicePort,
                },
              },
            ],
          },
        },
      ],
    },
  },
]

์ด ํŒŒ์ผ์—์„œ๋Š” ์„ธ ๊ฐ€์ง€ Kubernetes ์—”ํ„ฐํ‹ฐ๋ฅผ ํ•œ ๋ฒˆ์— ์„ค๋ช…ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ „๊ฐœ, ์˜ˆ๋ฐฐ ะธ ์ž…๊ตฌ. ์›ํ•œ๋‹ค๋ฉด ์ด๋ฅผ ๋‹ค๋ฅธ ๊ตฌ์„ฑ ์š”์†Œ์— ๋„ฃ์„ ์ˆ˜ ์žˆ์ง€๋งŒ ์ด ๋‹จ๊ณ„์—์„œ๋Š” ํ•˜๋‚˜๋งŒ ์žˆ์–ด๋„ ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.

๊ตฌ๋ฌธ jsonnet ์ผ๋ฐ˜ json๊ณผ ๋งค์šฐ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์›์น™์ ์œผ๋กœ ์ผ๋ฐ˜ json์€ ์ด๋ฏธ ์œ ํšจํ•œ jsonnet์ด๋ฏ€๋กœ ์ฒ˜์Œ์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์˜จ๋ผ์ธ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ์‰ฌ์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. yaml2json ์ผ๋ฐ˜์ ์ธ yaml์„ json์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ฑฐ๋‚˜ ๊ตฌ์„ฑ ์š”์†Œ์— ๋ณ€์ˆ˜๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์ง€ ์•Š์€ ๊ฒฝ์šฐ ์ผ๋ฐ˜ yaml ํ˜•์‹์œผ๋กœ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ž‘์—…ํ•  ๋•Œ jsonnet ํŽธ์ง‘๊ธฐ์šฉ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์„ค์น˜ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด vim์šฉ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์žˆ์Šต๋‹ˆ๋‹ค. vim-jsonnet, ๊ตฌ๋ฌธ ๊ฐ•์กฐ๋ฅผ ์ผœ๊ณ  ์ž๋™์œผ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. jsonnet fmt ์ €์žฅํ•  ๋•Œ๋งˆ๋‹ค(jsonnet ์„ค์น˜ ํ•„์š”)

๋ชจ๋“  ๊ฒƒ์ด ์ค€๋น„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ ๋ฐฐํฌ๋ฅผ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๊ฐ€ ์–ป์€ ๊ฒƒ์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ์„ ์‹คํ–‰ํ•ด ๋ด…์‹œ๋‹ค:

qbec show default

์ถœ๋ ฅ์—๋Š” ๊ธฐ๋ณธ ํด๋Ÿฌ์Šคํ„ฐ์— ์ ์šฉ๋  ๋ Œ๋”๋ง๋œ yaml ๋งค๋‹ˆํŽ˜์ŠคํŠธ๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

์ข‹์Šต๋‹ˆ๋‹ค. ์ด์ œ ์‹ ์ฒญํ•˜์„ธ์š”.

qbec apply default

์ถœ๋ ฅ์—์„œ ํด๋Ÿฌ์Šคํ„ฐ์—์„œ ์ˆ˜ํ–‰๋  ์ž‘์—…์„ ํ•ญ์ƒ ๋ณผ ์ˆ˜ ์žˆ์œผ๋ฉฐ, qbec๋Š” ๋‹ค์Œ์„ ์ž…๋ ฅํ•˜์—ฌ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์— ๋™์˜ํ•˜๋„๋ก ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค. y ๋‹น์‹ ์˜ ์˜๋„๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์šฐ๋ฆฌ์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ค€๋น„๋˜์–ด ๋ฐฐํฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!

๋ณ€๊ฒฝํ•˜๋Š” ๊ฒฝ์šฐ ์–ธ์ œ๋“ ์ง€ ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

qbec diff default

์ด๋Ÿฌํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ํ˜„์žฌ ๋ฐฐํฌ์— ์–ด๋–ค ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š”์ง€ ํ™•์ธํ•˜๋ ค๋ฉด

๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ปค๋ฐ‹ํ•˜๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋งˆ์„ธ์š”.

cd ../..
git add deploy/website
git commit -m "Add deploy for website"

5. Kubernetes-executor๋กœ Gitlab-runner ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ

์ตœ๊ทผ๊นŒ์ง€๋Š” ์ผ๋ฐ˜๋งŒ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ gitlab ๋Ÿฌ๋„ˆ ์‰˜ ๋˜๋Š” docker-executor๊ฐ€ ์žˆ๋Š” ์‚ฌ์ „ ์ค€๋น„๋œ ๋จธ์‹ (LXC ์ปจํ…Œ์ด๋„ˆ)์—์„œ. ์ฒ˜์Œ์—๋Š” gitlab์— ์ „์—ญ์ ์œผ๋กœ ์ •์˜๋œ ์—ฌ๋Ÿฌ ๋Ÿฌ๋„ˆ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋“ค์€ ๋ชจ๋“  ํ”„๋กœ์ ํŠธ์— ๋Œ€ํ•œ ๋„์ปค ์ด๋ฏธ์ง€๋ฅผ ์ˆ˜์ง‘ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์‹ค์Šต์—์„œ ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด ์ด ์˜ต์…˜์€ ์‹ค์šฉ์„ฑ๊ณผ ์•ˆ์ „์„ฑ ์ธก๋ฉด์—์„œ ๊ฐ€์žฅ ์ด์ƒ์ ์ธ ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. ๊ฐ ํ”„๋กœ์ ํŠธ ๋˜๋Š” ๊ฐ ํ™˜๊ฒฝ์— ๋Œ€ํ•ด ๋ณ„๋„์˜ ์‹คํ–‰๊ธฐ๋ฅผ ๋ฐฐํฌํ•˜๋Š” ๊ฒƒ์ด ํ›จ์”ฌ ๋” ์ข‹๊ณ  ์ด๋…์ ์œผ๋กœ ๋” ์ •ํ™•ํ•ฉ๋‹ˆ๋‹ค.

๋‹คํ–‰ํžˆ๋„ ์ด๋Š” ์ „ํ˜€ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด์ œ ์šฐ๋ฆฌ๋Š” ๋ฐฐํฌํ•  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. gitlab ๋Ÿฌ๋„ˆ Kubernetes์—์„œ ํ”„๋กœ์ ํŠธ์˜ ์ผ๋ถ€๋กœ ์ง์ ‘์ ์œผ๋กœ.

Gitlab์€ Kubernetes์— gitlab-runner๋ฅผ ๋ฐฐํฌํ•˜๊ธฐ ์œ„ํ•ด ๋ฏธ๋ฆฌ ๋งŒ๋“ค์–ด์ง„ Helm ์ฐจํŠธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ ๋‹น์‹ ์ด ํ•ด์•ผ ํ•  ์ผ์€ ์•Œ์•„๋‚ด๋Š” ๊ฒƒ๋ฟ์ž…๋‹ˆ๋‹ค ๋“ฑ๋ก ํ† ํฐ ์šฐ๋ฆฌ ํ”„๋กœ์ ํŠธ๋ฅผ ์œ„ํ•ด ์„ค์ • -> CI/CD -> ๋Ÿฐ๋„ˆ ๊ทธ๋ฆฌ๊ณ  ์ด๋ฅผ helm์— ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

helm repo add gitlab https://charts.gitlab.io

helm install gitlab-runner 
  --set gitlabUrl=https://gitlab.com 
  --set runnerRegistrationToken=yga8y-jdCusVDn_t4Wxc 
  --set rbac.create=true 
  gitlab/gitlab-runner

์žฅ์†Œ :

  • https://gitlab.com โ€” Gitlab ์„œ๋ฒ„์˜ ์ฃผ์†Œ์ž…๋‹ˆ๋‹ค.
  • yga8y-jdCusVDn_t4Wxc โ€” ํ”„๋กœ์ ํŠธ์˜ ๋“ฑ๋ก ํ† ํฐ.
  • rbac.create=true โ€” kubernetes-executor๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•œ ํฌ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ๊ถŒํ•œ์„ ์‹คํ–‰์ž์—๊ฒŒ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  ๊ฒƒ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์™„๋ฃŒ๋˜๋ฉด ํ•ด๋‹น ์„น์…˜์— ๋“ฑ๋ก๋œ ์ฃผ์ž๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ์ฃผ์ž, ํ”„๋กœ์ ํŠธ ์„ค์ •์—์„œ.

์ถ”๊ฐ€๋œ ์‹คํ–‰๊ธฐ์˜ ์Šคํฌ๋ฆฐ์ƒท

Kubernetes์—์„œ ๋ฐฐํฌ๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ  ์ž๋™ํ™”ํ•˜๊ธฐ ์œ„ํ•œ ์ƒˆ๋กœ์šด ๋„๊ตฌ ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ

๊ทธ๋ ‡๊ฒŒ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๊นŒ? - ๋„ค, ์ •๋ง ๊ฐ„๋‹จํ•ด์š”! ๋” ์ด์ƒ ์ˆ˜๋™์œผ๋กœ ๋Ÿฌ๋„ˆ๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€์ด ์—†์Šต๋‹ˆ๋‹ค. ์ด์ œ๋ถ€ํ„ฐ ๋Ÿฌ๋„ˆ๋Š” ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋˜๊ณ  ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค.

6. QBEC๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Helm ์ฐจํŠธ ๋ฐฐํฌ

์šฐ๋ฆฌ๊ฐ€ ๊ณ ๋ คํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— gitlab ๋Ÿฌ๋„ˆ ํ”„๋กœ์ ํŠธ์˜ ์ผ๋ถ€์ด๋ฏ€๋กœ Git ์ €์žฅ์†Œ์— ์„ค๋ช…ํ•  ์ฐจ๋ก€์ž…๋‹ˆ๋‹ค.

๋ณ„๋„์˜ ๊ตฌ์„ฑ ์š”์†Œ๋กœ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์›น ์‚ฌ์ดํŠธํ•˜์ง€๋งŒ ์•ž์œผ๋กœ๋Š” ๋‹ค๋ฅธ ๋ณต์‚ฌ๋ณธ์„ ๋ฐฐํฌํ•  ๊ณ„ํš์ž…๋‹ˆ๋‹ค. ์›น ์‚ฌ์ดํŠธ ๋งค์šฐ ์ž์ฃผ, ๋‹ฌ๋ฆฌ gitlab ๋Ÿฌ๋„ˆ, ์ด๋Š” Kubernetes ํด๋Ÿฌ์Šคํ„ฐ๋‹น ํ•œ ๋ฒˆ๋งŒ ๋ฐฐํฌ๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด๋ฅผ ์œ„ํ•œ ๋ณ„๋„์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ดˆ๊ธฐํ™”ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

cd deploy
qbec init gitlab-runner
cd gitlab-runner

์ด๋ฒˆ์—๋Š” Kubernetes ์—”ํ„ฐํ‹ฐ๋ฅผ ์ˆ˜๋™์œผ๋กœ ์„ค๋ช…ํ•˜์ง€ ์•Š๊ณ  ๋ฏธ๋ฆฌ ๋งŒ๋“ค์–ด์ง„ Helm ์ฐจํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. qbec์˜ ์žฅ์  ์ค‘ ํ•˜๋‚˜๋Š” Git ์ €์žฅ์†Œ์—์„œ ์ง์ ‘ Helm ์ฐจํŠธ๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

git ํ•˜์œ„ ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜์—ฌ ์—ฐ๊ฒฐํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

git submodule add https://gitlab.com/gitlab-org/charts/gitlab-runner vendor/gitlab-runner

์ด์ œ ๋””๋ ‰ํ† ๋ฆฌ ๋ฒค๋”/gitlab-runner gitlab-runner์— ๋Œ€ํ•œ ์ฐจํŠธ๊ฐ€ ์žˆ๋Š” ์ €์žฅ์†Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๋น„์Šทํ•œ ๋ฐฉ์‹์œผ๋กœ ๋‹ค๋ฅธ ์ €์žฅ์†Œ(์˜ˆ: ๊ณต์‹ ์ฐจํŠธ๊ฐ€ ์žˆ๋Š” ์ „์ฒด ์ €์žฅ์†Œ)๋ฅผ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. https://github.com/helm/charts

๊ตฌ์„ฑํ’ˆ์„ ์„ค๋ช…ํ•˜์ž๋ฉด ๊ตฌ์„ฑ ์š”์†Œ/gitlab-runner.jsonnet:

local env = {
  name: std.extVar('qbec.io/env'),
  namespace: std.extVar('qbec.io/defaultNs'),
};
local p = import '../params.libsonnet';
local params = p.components.gitlabRunner;

std.native('expandHelmTemplate')(
  '../vendor/gitlab-runner',
  params.values,
  {
    nameTemplate: params.name,
    namespace: env.namespace,
    thisFile: std.thisFile,
    verbose: true,
  }
)

์ฒซ ๋ฒˆ์งธ ์ฃผ์žฅ ExpandHelmTemplate ์ฐจํŠธ์˜ ๊ฒฝ๋กœ๋ฅผ ์ „๋‹ฌํ•œ ๋‹ค์Œ ๋งค๊ฐœ๋ณ€์ˆ˜.๊ฐ’, ํ™˜๊ฒฝ ๋งค๊ฐœ๋ณ€์ˆ˜์—์„œ ๊ฐ€์ ธ์˜จ ๋‹ค์Œ

  • ์ด๋ฆ„ํ…œํ”Œ๋ฆฟ โ€” ๋ฆด๋ฆฌ์Šค ์ด๋ฆ„
  • ๋„ค์ž„ ์ŠคํŽ˜์ด์Šค โ€” helm์œผ๋กœ ๋„ค์ž„์ŠคํŽ˜์ด์Šค๊ฐ€ ์ „์†ก๋จ
  • ์ด ํŒŒ์ผ โ€” ํ˜„์žฌ ํŒŒ์ผ์˜ ๊ฒฝ๋กœ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ํ•„์ˆ˜ ๋งค๊ฐœ๋ณ€์ˆ˜
  • ๋ง ์ˆ˜๊ฐ€ ๋งŽ์€ - ๋ช…๋ น์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค ์กฐํƒ€ ํ…œํ”Œ๋ฆฟ ์ฐจํŠธ๋ฅผ ๋ Œ๋”๋งํ•  ๋•Œ ๋ชจ๋“  ์ธ์ˆ˜ ํฌํ•จ

์ด์ œ ๊ตฌ์„ฑ์š”์†Œ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ํ™˜๊ฒฝ/base.libsonnet:

local secrets = import '../secrets/base.libsonnet';

{
  components: {
    gitlabRunner: {
      name: 'gitlab-runner',
      values: {
        gitlabUrl: 'https://gitlab.com/',
        rbac: {
          create: true,
        },
        runnerRegistrationToken: secrets.runnerRegistrationToken,
      },
    },
  },
}

์ฃผ์˜ ๋Ÿฌ๋„ˆ๋“ฑ๋กํ† ํฐ ์šฐ๋ฆฌ๋Š” ์™ธ๋ถ€ ํŒŒ์ผ์—์„œ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค ๋น„๋ฐ€/base.libsonnet, ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

{
  runnerRegistrationToken: 'yga8y-jdCusVDn_t4Wxc',
}

๋ชจ๋“  ๊ฒƒ์ด ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

qbec show default

๋ชจ๋“  ๊ฒƒ์ด ์ •์ƒ์ด๋ฉด Helm์„ ํ†ตํ•ด ์ด์ „์— ๋ฐฐํฌํ•œ ๋ฆด๋ฆฌ์Šค๋ฅผ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

helm uninstall gitlab-runner

๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ๋ฐฐํฌํ•˜๋˜, qbec์„ ํ†ตํ•ด:

qbec apply default

7. git-crypt ์†Œ๊ฐœ

ํž˜๋‚ด ์•”ํ˜ธํ™” ์ €์žฅ์†Œ์— ๋Œ€ํ•œ ํˆฌ๋ช…ํ•œ ์•”ํ˜ธํ™”๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

ํ˜„์žฌ gitlab-runner์˜ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

.
โ”œโ”€โ”€ components
โ”‚   โ”œโ”€โ”€ gitlab-runner.jsonnet
โ”œโ”€โ”€ environments
โ”‚   โ”œโ”€โ”€ base.libsonnet
โ”‚   โ””โ”€โ”€ default.libsonnet
โ”œโ”€โ”€ params.libsonnet
โ”œโ”€โ”€ qbec.yaml
โ”œโ”€โ”€ secrets
โ”‚   โ””โ”€โ”€ base.libsonnet
โ””โ”€โ”€ vendor
    โ””โ”€โ”€ gitlab-runner (submodule)

ํ•˜์ง€๋งŒ Git์— ๋น„๋ฐ€์„ ์ €์žฅํ•˜๋Š” ๊ฒƒ์€ ์•ˆ์ „ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ฃ ? ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” ๊ทธ๊ฒƒ๋“ค์„ ์ ์ ˆํ•˜๊ฒŒ ์•”ํ˜ธํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ผ๋ฐ˜์ ์œผ๋กœ ํ•˜๋‚˜์˜ ๋ณ€์ˆ˜ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์ด ํ•ญ์ƒ ์˜๋ฏธ๊ฐ€ ์žˆ๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. ๋‹ค์Œ์œผ๋กœ ๋น„๋ฐ€์„ ์ „์†กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ๋ฒก CI ์‹œ์Šคํ…œ์˜ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ํ†ตํ•ด.
๊ทธ๋Ÿฌ๋‚˜ ๋” ๋งŽ์€ ๋น„๋ฐ€์„ ํฌํ•จํ•  ์ˆ˜ ์žˆ๋Š” ๋” ๋ณต์žกํ•œ ํ”„๋กœ์ ํŠธ๋„ ์žˆ๋‹ค๋Š” ์ ์€ ์ฃผ๋ชฉํ•  ๊ฐ€์น˜๊ฐ€ ์žˆ์œผ๋ฉฐ, ์ด๋ฅผ ๋ชจ๋‘ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ํ†ตํ•ด ์ „์†กํ•˜๋Š” ๊ฒƒ์€ ๋งค์šฐ ์–ด๋ ค์šธ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ฒŒ๋‹ค๊ฐ€ ์ด ๊ฒฝ์šฐ์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ›Œ๋ฅญํ•œ ๋„๊ตฌ์— ๋Œ€ํ•ด ๋งํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ž์‹ ์•”ํ˜ธ.

์ž์‹ ์•”ํ˜ธ Git์˜ ๊ฒฝ์šฐ์™€ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ๋น„๋ฐ€์˜ ์ „์ฒด ๊ธฐ๋ก์„ ์ €์žฅํ•˜๊ณ  ์ถฉ๋Œ์„ ๋น„๊ต, ๋ณ‘ํ•ฉ ๋ฐ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์—์„œ๋„ ํŽธ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

์„ค์น˜ ํ›„ ๊ฐ€์žฅ ๋จผ์ € ํ•  ์ผ์€ ์ž์‹ ์•”ํ˜ธ ์ €์žฅ์†Œ์— ๋Œ€ํ•œ ํ‚ค๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

git crypt init

PGP ํ‚ค๊ฐ€ ์žˆ์œผ๋ฉด ์ฆ‰์‹œ ์ด ํ”„๋กœ์ ํŠธ์˜ ๊ณต๋™ ์ž‘์—…์ž๋กœ ์ž์‹ ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

git-crypt add-gpg-user [email protected]

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์–ธ์ œ๋“ ์ง€ ๊ฐœ์ธ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด ์ €์žฅ์†Œ์˜ ์•”ํ˜ธ๋ฅผ ํ•ด๋…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

PGP ํ‚ค๊ฐ€ ์—†๊ณ  ์˜ˆ์ƒํ•˜์ง€ ๋ชปํ•œ ๊ฒฝ์šฐ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์œผ๋กœ ํ”„๋กœ์ ํŠธ ํ‚ค๋ฅผ ๋‚ด๋ณด๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

git crypt export-key /path/to/keyfile

๋”ฐ๋ผ์„œ ์ˆ˜์ถœ์„ ํ•œ ์‚ฌ๋žŒ์€ ๋ˆ„๊ตฌ๋‚˜ ํ‚ค ํŒŒ์ผ ์ €์žฅ์†Œ๋ฅผ ํ•ด๋…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด์ œ ์ฒซ ๋ฒˆ์งธ ๋น„๋ฐ€์„ ์„ค์ •ํ•  ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค.
์šฐ๋ฆฌ๊ฐ€ ์•„์ง ๋””๋ ‰ํ† ๋ฆฌ์— ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์ƒ๊ธฐ์‹œ์ผœ ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ฐฐํฌ/gitlab-runner/, ๋””๋ ‰ํ† ๋ฆฌ๊ฐ€ ์žˆ๋Š” ๊ณณ ๋น„๋ฐ€/, ๊ทธ ์•ˆ์— ์žˆ๋Š” ๋ชจ๋“  ํŒŒ์ผ์„ ์•”ํ˜ธํ™”ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋น„๋ฐ€/.gitattributes ๋‹ค์Œ ๋‚ด์šฉ์œผ๋กœ:

* filter=git-crypt diff=git-crypt
.gitattributes !filter !diff

๋‚ด์šฉ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ์ด ๋ชจ๋“  ํŒŒ์ผ์ด ๋งˆ์Šคํฌ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. * ํ†ตํ•ด ๊ตฌ๋™๋ฉ๋‹ˆ๋‹ค ์ž์‹ ์•”ํ˜ธ, ๋Œ€๋ถ€๋ถ„์„ ์ œ์™ธํ•˜๊ณ  .git์†์„ฑ

๋‹ค์Œ์„ ์‹คํ–‰ํ•˜์—ฌ ์ด๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

git crypt status -e

์ถœ๋ ฅ์€ ์•”ํ˜ธํ™”๊ฐ€ ํ™œ์„ฑํ™”๋œ ์ €์žฅ์†Œ์˜ ๋ชจ๋“  ํŒŒ์ผ ๋ชฉ๋ก์ž…๋‹ˆ๋‹ค.

๊ทธ๊ฒŒ ๋‹ค์ž…๋‹ˆ๋‹ค. ์ด์ œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์•ˆ์ „ํ•˜๊ฒŒ ์ปค๋ฐ‹ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

cd ../..
git add .
git commit -m "Add deploy for gitlab-runner"

์ €์žฅ์†Œ๋ฅผ ์ฐจ๋‹จํ•˜๋ ค๋ฉด ๋‹ค์Œ์„ ์‹คํ–‰ํ•˜์„ธ์š”.

git crypt lock

์•”ํ˜ธํ™”๋œ ๋ชจ๋“  ํŒŒ์ผ์€ ์ฆ‰์‹œ ๋ฐ”์ด๋„ˆ๋ฆฌ๋กœ ๋ณ€ํ™˜๋˜์–ด ์ฝ์„ ์ˆ˜ ์—†๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
์ €์žฅ์†Œ๋ฅผ ๋ณตํ˜ธํ™”ํ•˜๋ ค๋ฉด ๋‹ค์Œ์„ ์‹คํ–‰ํ•˜์„ธ์š”.

git crypt unlock

8. ๋„๊ตฌ ์ƒ์ž ์ด๋ฏธ์ง€ ๋งŒ๋“ค๊ธฐ

๋„๊ตฌ ์ƒ์ž ์ด๋ฏธ์ง€๋Š” ํ”„๋กœ์ ํŠธ๋ฅผ ๋ฐฐํฌํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ๋ชจ๋“  ๋„๊ตฌ๊ฐ€ ํฌํ•จ๋œ ์ด๋ฏธ์ง€์ž…๋‹ˆ๋‹ค. Gitlab ์‹คํ–‰๊ธฐ๊ฐ€ ์ผ๋ฐ˜์ ์ธ ๋ฐฐํฌ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ๋Š” ๋ชจ๋“  ๊ฒƒ์ด ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. ์ƒˆ ๊ฒƒ์„ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. dockerfiles/๋„๊ตฌ ์ƒ์ž/Dockerfile ๋‹ค์Œ ๋‚ด์šฉ์œผ๋กœ:

FROM alpine:3.11

RUN apk add --no-cache git git-crypt

RUN QBEC_VER=0.10.3 
 && wget -O- https://github.com/splunk/qbec/releases/download/v${QBEC_VER}/qbec-linux-amd64.tar.gz 
     | tar -C /tmp -xzf - 
 && mv /tmp/qbec /tmp/jsonnet-qbec /usr/local/bin/

RUN KUBECTL_VER=1.17.0 
 && wget -O /usr/local/bin/kubectl 
      https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VER}/bin/linux/amd64/kubectl 
 && chmod +x /usr/local/bin/kubectl

RUN HELM_VER=3.0.2 
 && wget -O- https://get.helm.sh/helm-v${HELM_VER}-linux-amd64.tar.gz 
     | tar -C /tmp -zxf - 
 && mv /tmp/linux-amd64/helm /usr/local/bin/helm

๋ณด์‹œ๋‹ค์‹œํ”ผ ์ด ์ด๋ฏธ์ง€์—์„œ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ฐฐํฌํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•œ ๋ชจ๋“  ์œ ํ‹ธ๋ฆฌํ‹ฐ๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ฟ  ๋ฒ ํ‹€, ๊ทธ๋Ÿฌ๋‚˜ ํŒŒ์ดํ”„๋ผ์ธ ์„ค์ • ๋‹จ๊ณ„์—์„œ ์ด๋ฅผ ๊ฐ€์ง€๊ณ  ๋†€๊ณ  ์‹ถ์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ Kubernetes์™€ ํ†ต์‹ ํ•˜๊ณ  ๋ฐฐํฌํ•˜๋ ค๋ฉด gitlab-runner์—์„œ ์ƒ์„ฑ๋œ Pod์— ๋Œ€ํ•œ ์—ญํ• ์„ ๊ตฌ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด๋ฅผ ์œ„ํ•ด gitlab-runner๊ฐ€ ์žˆ๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ๋กœ ์ด๋™ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

cd deploy/gitlab-runner

๊ทธ๋ฆฌ๊ณ  ์ƒˆ๋กœ์šด ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜์„ธ์š” ๊ตฌ์„ฑ ์š”์†Œ/rbac.jsonnet:

local env = {
  name: std.extVar('qbec.io/env'),
  namespace: std.extVar('qbec.io/defaultNs'),
};
local p = import '../params.libsonnet';
local params = p.components.rbac;

[
  {
    apiVersion: 'v1',
    kind: 'ServiceAccount',
    metadata: {
      labels: {
        app: params.name,
      },
      name: params.name,
    },
  },
  {
    apiVersion: 'rbac.authorization.k8s.io/v1',
    kind: 'Role',
    metadata: {
      labels: {
        app: params.name,
      },
      name: params.name,
    },
    rules: [
      {
        apiGroups: [
          '*',
        ],
        resources: [
          '*',
        ],
        verbs: [
          '*',
        ],
      },
    ],
  },
  {
    apiVersion: 'rbac.authorization.k8s.io/v1',
    kind: 'RoleBinding',
    metadata: {
      labels: {
        app: params.name,
      },
      name: params.name,
    },
    roleRef: {
      apiGroup: 'rbac.authorization.k8s.io',
      kind: 'Role',
      name: params.name,
    },
    subjects: [
      {
        kind: 'ServiceAccount',
        name: params.name,
        namespace: env.namespace,
      },
    ],
  },
]

๋˜ํ•œ ํ™˜๊ฒฝ/base.libsonnet, ์ด์ œ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

local secrets = import '../secrets/base.libsonnet';

{
  components: {
    gitlabRunner: {
      name: 'gitlab-runner',
      values: {
        gitlabUrl: 'https://gitlab.com/',
        rbac: {
          create: true,
        },
        runnerRegistrationToken: secrets.runnerRegistrationToken,
        runners: {
          serviceAccountName: $.components.rbac.name,
          image: 'registry.gitlab.com/kvaps/docs.example.org/toolbox:v0.0.1',
        },
      },
    },
    rbac: {
      name: 'gitlab-runner-deploy',
    },
  },
}

์ฃผ์˜ $.comComponents.rbac.name ~์„ ์ฐธ๊ณ ํ•˜์—ฌ name ๊ตฌ์„ฑ์š”์†Œ์šฉ ๋ฐฑํŒฉ

๋ฌด์—‡์ด ๋ณ€๊ฒฝ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

qbec diff default

Kubernetes์— ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

qbec apply default

๋˜ํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ git์— ์ปค๋ฐ‹ํ•˜๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋งˆ์„ธ์š”.

cd ../..
git add dockerfiles/toolbox
git commit -m "Add Dockerfile for toolbox"
git add deploy/gitlab-runner
git commit -m "Configure gitlab-runner to use toolbox"

9. ์ฒซ ๋ฒˆ์งธ ํŒŒ์ดํ”„๋ผ์ธ๊ณผ ํƒœ๊ทธ๋ณ„ ์ด๋ฏธ์ง€ ์กฐ๋ฆฝ

ํ”„๋กœ์ ํŠธ์˜ ๋ฃจํŠธ์—์„œ ์šฐ๋ฆฌ๋Š” ๋งŒ๋“ค ๊ฒƒ์ž…๋‹ˆ๋‹ค .gitlab-ci.yml ๋‹ค์Œ ๋‚ด์šฉ์œผ๋กœ:

.build_docker_image:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:debug-v0.15.0
    entrypoint: [""]
  before_script:
    - echo "{"auths":{"$CI_REGISTRY":{"username":"$CI_REGISTRY_USER","password":"$CI_REGISTRY_PASSWORD"}}}" > /kaniko/.docker/config.json

build_toolbox:
  extends: .build_docker_image
  script:
    - /kaniko/executor --cache --context $CI_PROJECT_DIR/dockerfiles/toolbox --dockerfile $CI_PROJECT_DIR/dockerfiles/toolbox/Dockerfile --destination $CI_REGISTRY_IMAGE/toolbox:$CI_COMMIT_TAG
  only:
    refs:
      - tags

build_website:
  extends: .build_docker_image
  variables:
    GIT_SUBMODULE_STRATEGY: normal
  script:
    - /kaniko/executor --cache --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/dockerfiles/website/Dockerfile --destination $CI_REGISTRY_IMAGE/website:$CI_COMMIT_TAG
  only:
    refs:
      - tags

์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ์ฐธ๊ณ ํ•˜์‹ญ์‹œ์˜ค GIT_SUBMODULE_STRATEGY: ์ •์ƒ ์‹คํ–‰ ์ „์— ํ•˜์œ„ ๋ชจ๋“ˆ์„ ๋ช…์‹œ์ ์œผ๋กœ ์ดˆ๊ธฐํ™”ํ•ด์•ผ ํ•˜๋Š” ์ž‘์—…์˜ ๊ฒฝ์šฐ.

๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ปค๋ฐ‹ํ•˜๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋งˆ์„ธ์š”.

git add .gitlab-ci.yml
git commit -m "Automate docker build"

๋‚˜๋Š” ์ด๊ฒƒ์„ ๋ฒ„์ „์ด๋ผ๊ณ  ์•ˆ์ „ํ•˜๊ฒŒ ๋ถ€๋ฅผ ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. v0.0.1 ํƒœ๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

git tag v0.0.1

์ƒˆ ๋ฒ„์ „์„ ์ถœ์‹œํ•ด์•ผ ํ•  ๋•Œ๋งˆ๋‹ค ํƒœ๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. Docker ์ด๋ฏธ์ง€์˜ ํƒœ๊ทธ๋Š” Git ํƒœ๊ทธ์— ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค. ์ƒˆ ํƒœ๊ทธ๊ฐ€ ํฌํ•จ๋œ ๊ฐ ํ‘ธ์‹œ๋Š” ์ด ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฏธ์ง€ ๋นŒ๋“œ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.

ํ•ด๋ณด์ž ์ž์‹ ํ‘ธ์‹œ --ํƒœ๊ทธ, ์ฒซ ๋ฒˆ์งธ ํŒŒ์ดํ”„๋ผ์ธ์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์ฒซ ๋ฒˆ์งธ ํŒŒ์ดํ”„๋ผ์ธ์˜ ์Šคํฌ๋ฆฐ์ƒท

Kubernetes์—์„œ ๋ฐฐํฌ๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ  ์ž๋™ํ™”ํ•˜๊ธฐ ์œ„ํ•œ ์ƒˆ๋กœ์šด ๋„๊ตฌ ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ

ํƒœ๊ทธ๋ฅผ ํ†ตํ•œ ์–ด์…ˆ๋ธ”๋ฆฌ๋Š” Docker ์ด๋ฏธ์ง€ ๊ตฌ์ถ•์—๋Š” ์ ํ•ฉํ•˜์ง€๋งŒ Kubernetes์— ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ฐฐํฌํ•˜๋Š” ๋ฐ๋Š” ์ ํ•ฉํ•˜์ง€ ์•Š๋‹ค๋Š” ์‚ฌ์‹ค์— ์ฃผ๋ชฉํ•  ๊ฐ€์น˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์ „ ์ปค๋ฐ‹์— ์ƒˆ ํƒœ๊ทธ๋ฅผ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ด ๊ฒฝ์šฐ ํ•ด๋‹น ํƒœ๊ทธ์— ๋Œ€ํ•œ ํŒŒ์ดํ”„๋ผ์ธ์„ ์ดˆ๊ธฐํ™”ํ•˜๋ฉด ์ด์ „ ๋ฒ„์ „์ด ๋ฐฐํฌ๋ฉ๋‹ˆ๋‹ค.

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ผ๋ฐ˜์ ์œผ๋กœ ๋„์ปค ์ด๋ฏธ์ง€ ๋นŒ๋“œ๊ฐ€ ํƒœ๊ทธ์— ์—ฐ๊ฒฐ๋˜๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ถ„๊ธฐ์— ๋ฐฐํฌ๋ฉ๋‹ˆ๋‹ค. ์„์‚ฌ, ์ˆ˜์ง‘๋œ ์ด๋ฏธ์ง€์˜ ๋ฒ„์ „์ด ํ•˜๋“œ์ฝ”๋”ฉ๋ฉ๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•œ ๋˜๋Œ๋ฆฌ๊ธฐ๋กœ ๋กค๋ฐฑ์„ ์ดˆ๊ธฐํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ๊ณณ์ž…๋‹ˆ๋‹ค. ์„์‚ฌ-๊ฐ€์ง€.

10. ๋ฐฐํฌ ์ž๋™ํ™”

Gitlab-runner๊ฐ€ ๋น„๋ฐ€์„ ํ•ด๋…ํ•˜๋ ค๋ฉด ์ €์žฅ์†Œ ํ‚ค๋ฅผ ๋‚ด๋ณด๋‚ด๊ณ  ์ด๋ฅผ CI ํ™˜๊ฒฝ ๋ณ€์ˆ˜์— ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

git crypt export-key /tmp/docs-repo.key
base64 -w0 /tmp/docs-repo.key; echo

๊ฒฐ๊ณผ ์ค„์„ Gitlab์— ์ €์žฅํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ํ”„๋กœ์ ํŠธ ์„ค์ •์œผ๋กœ ์ด๋™ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
์„ค์ • -> CI/CD -> ๋ณ€์ˆ˜

๊ทธ๋ฆฌ๊ณ  ์ƒˆ ๋ณ€์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

ํƒ€์ž…
ํ‚ค
๊ฐ€์น˜๊ด€
๋ณดํ˜ธ
๊ฐ€๋ฉด
๋ฒ”์œ„

File
GITCRYPT_KEY
<your string>
true (ํ›ˆ๋ จ ์ค‘์— ๋‹น์‹ ์€ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค false)
true
All environments

์ถ”๊ฐ€๋œ ๋ณ€์ˆ˜์˜ ์Šคํฌ๋ฆฐ์ƒท

Kubernetes์—์„œ ๋ฐฐํฌ๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ  ์ž๋™ํ™”ํ•˜๊ธฐ ์œ„ํ•œ ์ƒˆ๋กœ์šด ๋„๊ตฌ ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ

์ด์ œ ์—…๋ฐ์ดํŠธํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. .gitlab-ci.yml ๊ทธ๊ฒƒ์— ์ถ”๊ฐ€ :

.deploy_qbec_app:
  stage: deploy
  only:
    refs:
      - master

deploy_gitlab_runner:
  extends: .deploy_qbec_app
  variables:
    GIT_SUBMODULE_STRATEGY: normal
  before_script:
    - base64 -d "$GITCRYPT_KEY" | git-crypt unlock -
  script:
    - qbec apply default --root deploy/gitlab-runner --force:k8s-context __incluster__ --wait --yes

deploy_website:
  extends: .deploy_qbec_app
  script:
    - qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yes

์—ฌ๊ธฐ์—์„œ๋Š” qbec์— ๋Œ€ํ•œ ๋ช‡ ๊ฐ€์ง€ ์ƒˆ๋กœ์šด ์˜ต์…˜์„ ํ™œ์„ฑํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.

  • --์ผ๋ถ€/์•ฑ ๋ฃจํŠธ โ€” ํŠน์ • ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์˜ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • --force:k8s-์ปจํ…์ŠคํŠธ __incluster__ - ์ด๋Š” gtilab-runner๊ฐ€ ์‹คํ–‰ ์ค‘์ธ ๋™์ผํ•œ ํด๋Ÿฌ์Šคํ„ฐ์—์„œ ๋ฐฐํฌ๊ฐ€ ๋ฐœ์ƒํ•จ์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋งˆ๋ฒ• ๋ณ€์ˆ˜์ž…๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด qbec์ด kubeconfig์—์„œ ์ ํ•ฉํ•œ Kubernetes ์„œ๋ฒ„๋ฅผ ์ฐพ์œผ๋ ค๊ณ  ์‹œ๋„ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Š” ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  • --๊ธฐ๋‹ค๋ฆฌ๋‹ค โ€” qbec์ด ์ƒ์„ฑํ•œ ๋ฆฌ์†Œ์Šค๊ฐ€ ์ค€๋น„ ์ƒํƒœ๊ฐ€ ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฐ ๋‹ค์Œ ์„ฑ๊ณต์ ์ธ ์ข…๋ฃŒ ์ฝ”๋“œ๋กœ ์ข…๋ฃŒํ•˜๋„๋ก ๊ฐ•์ œํ•ฉ๋‹ˆ๋‹ค.
  • -์˜ˆ - ๋‹จ์ˆœํžˆ ๋Œ€ํ™”ํ˜• ์‰˜์„ ๋น„ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค. ํ™•์‹คํ•ฉ๋‹ˆ๊นŒ? ๋ฐฐํฌํ•  ๋•Œ.

๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ปค๋ฐ‹ํ•˜๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋งˆ์„ธ์š”.

git add .gitlab-ci.yml
git commit -m "Automate deploy"

๊ทธ๋ฆฌ๊ณ  ํ›„์— git push ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์–ด๋–ป๊ฒŒ ๋ฐฐํฌ๋˜์—ˆ๋Š”์ง€ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋‘ ๋ฒˆ์งธ ํŒŒ์ดํ”„๋ผ์ธ์˜ ์Šคํฌ๋ฆฐ์ƒท

Kubernetes์—์„œ ๋ฐฐํฌ๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ  ์ž๋™ํ™”ํ•˜๊ธฐ ์œ„ํ•œ ์ƒˆ๋กœ์šด ๋„๊ตฌ ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ

11. ๋งˆ์Šคํ„ฐ๋กœ ํ‘ธ์‹œํ•  ๋•Œ ์•„ํ‹ฐํŒฉํŠธ ๋ฐ ์–ด์…ˆ๋ธ”๋ฆฌ

์ผ๋ฐ˜์ ์œผ๋กœ ์œ„์— ์„ค๋ช…๋œ ๋‹จ๊ณ„๋Š” ๊ฑฐ์˜ ๋ชจ๋“  ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ  ์ œ๊ณตํ•˜๋Š” ๋ฐ ์ถฉ๋ถ„ํ•˜์ง€๋งŒ ์‚ฌ์ดํŠธ๋ฅผ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•  ๋•Œ๋งˆ๋‹ค ํƒœ๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ณด๋‹ค ๋™์ ์ธ ๊ฒฝ๋กœ๋ฅผ ์„ ํƒํ•˜๊ณ  ๋งˆ์Šคํ„ฐ ๋ธŒ๋žœ์น˜์— ๋‹ค์ด์ œ์ŠคํŠธ ๋ฐฐํฌ๋ฅผ ์„ค์ •ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์•„์ด๋””์–ด๋Š” ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. ์ด์ œ ์šฐ๋ฆฌ์˜ ์ด๋ฏธ์ง€๋Š” ์›น ์‚ฌ์ดํŠธ ๋ฐ€์–ด๋„ฃ์„ ๋•Œ๋งˆ๋‹ค ๋‹ค์‹œ ์ž‘์„ฑ๋ฉ๋‹ˆ๋‹ค. ์„์‚ฌ, Kubernetes์— ์ž๋™์œผ๋กœ ๋ฐฐํฌ๋ฉ๋‹ˆ๋‹ค.

์ด ๋‘ ๊ฐ€์ง€ ์ž‘์—…์„ ์—…๋ฐ์ดํŠธํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. .gitlab-ci.yml:

build_website:
  extends: .build_docker_image
  variables:
    GIT_SUBMODULE_STRATEGY: normal
  script:
    - mkdir -p $CI_PROJECT_DIR/artifacts
    - /kaniko/executor --cache --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/dockerfiles/website/Dockerfile --destination $CI_REGISTRY_IMAGE/website:$CI_COMMIT_REF_NAME --digest-file $CI_PROJECT_DIR/artifacts/website.digest
  artifacts:
    paths:
      - artifacts/
  only:
    refs:
      - master
      - tags

deploy_website:
  extends: .deploy_qbec_app
  script:
    - DIGEST="$(cat artifacts/website.digest)"
    - qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST"

์Šค๋ ˆ๋“œ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ์„์‚ฌ ะบ ์‹ฌํŒ ์ผ์ž๋ฆฌ๋ฅผ ์œ„ํ•ด build_website ์ด์ œ ์šฐ๋ฆฌ๋Š” $CI_COMMIT_REF_NAME ๋Œ€์‹  $CI_COMMIT_TAG์ฆ‰, Git์˜ ํƒœ๊ทธ์—์„œ ์—ฐ๊ฒฐ์ด ํ•ด์ œ๋˜์—ˆ์œผ๋ฉฐ ์ด์ œ ํŒŒ์ดํ”„๋ผ์ธ์„ ์ดˆ๊ธฐํ™”ํ•œ ์ปค๋ฐ‹ ๋ธŒ๋žœ์น˜์˜ ์ด๋ฆ„์œผ๋กœ ์ด๋ฏธ์ง€๋ฅผ ํ‘ธ์‹œํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ํƒœ๊ทธ์™€ ํ•จ๊ป˜ ์ž‘๋™ํ•˜๋ฏ€๋กœ docker-registry์˜ ํŠน์ • ๋ฒ„์ „์œผ๋กœ ์‚ฌ์ดํŠธ์˜ ์Šค๋ƒ…์ƒท์„ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์€ ์ฃผ๋ชฉํ•  ๊ฐ€์น˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์ดํŠธ์˜ ์ƒˆ ๋ฒ„์ „์— ๋Œ€ํ•œ Docker ํƒœ๊ทธ ์ด๋ฆ„์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ์—๋„ Kubernetes์— ๋Œ€ํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์„ค๋ช…ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์ƒˆ ์ด๋ฏธ์ง€์—์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์žฌ๋ฐฐํฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ฐฐํฌ ๋งค๋‹ˆํŽ˜์ŠคํŠธ.

์˜ต์…˜ โ€”vm:ext-str ๋‹ค์ด์ œ์ŠคํŠธ=โ€$DIGESTโ€ qbec์˜ ๊ฒฝ์šฐ - ์™ธ๋ถ€ ๋ณ€์ˆ˜๋ฅผ jsonnet์— ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋ฆด๋ฆฌ์Šค๋  ๋•Œ๋งˆ๋‹ค ํด๋Ÿฌ์Šคํ„ฐ์— ์ด๋ฅผ ์žฌ๋ฐฐํฌํ•˜๊ธฐ๋ฅผ ์›ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฏธ์ง€์˜ ํŠน์ • ๋ฒ„์ „์— ์—ฐ๊ฒฐํ•˜๊ณ  ๋ณ€๊ฒฝ ์‹œ ๋ฐฐํฌ๋ฅผ ํŠธ๋ฆฌ๊ฑฐํ•ด์•ผ ํ•˜๋ฏ€๋กœ ์ด์ œ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋Š” ํƒœ๊ทธ ์ด๋ฆ„์„ ๋” ์ด์ƒ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์—์„œ๋Š” ๋‹ค์ด์ œ์ŠคํŠธ ์ด๋ฏธ์ง€๋ฅผ ํŒŒ์ผ์— ์ €์žฅํ•˜๋Š” Kaniko์˜ ๊ธฐ๋Šฅ์„ ํ†ตํ•ด ๋„์›€์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์˜ต์…˜ --๋‹ค์ด์ œ์ŠคํŠธ ํŒŒ์ผ)
๊ทธ๋Ÿฐ ๋‹ค์Œ ์ด ํŒŒ์ผ์„ ์ „์†กํ•˜๊ณ  ๋ฐฐํฌ ์‹œ ์ฝ์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์—…๋ฐ์ดํŠธํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ฐฐํฌ/์›น์‚ฌ์ดํŠธ/ํ™˜๊ฒฝ/base.libsonnet ์ด์ œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณด์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค:

{
  components: {
    website: {
      name: 'example-docs',
      image: 'registry.gitlab.com/kvaps/docs.example.org/website@' + std.extVar('digest'),
      replicas: 1,
      containerPort: 80,
      servicePort: 80,
      nodeSelector: {},
      tolerations: [],
      ingressClass: 'nginx',
      domain: 'docs.example.org',
    },
  },
}

์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ ๋ชจ๋“  ์ปค๋ฐ‹์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์„์‚ฌ ๋„์ปค ์ด๋ฏธ์ง€ ๋นŒ๋“œ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค. ์›น ์‚ฌ์ดํŠธ, Kubernetes์— ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค.

๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ปค๋ฐ‹ํ•˜๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋งˆ์„ธ์š”.

git add .
git commit -m "Configure dynamic build"

๋‚˜์ค‘์— ํ™•์ธํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค git push ์šฐ๋ฆฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒƒ์„ ๋ณด๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค:

๋งˆ์Šคํ„ฐ์šฉ ํŒŒ์ดํ”„๋ผ์ธ ์Šคํฌ๋ฆฐ์ƒท

Kubernetes์—์„œ ๋ฐฐํฌ๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ  ์ž๋™ํ™”ํ•˜๊ธฐ ์œ„ํ•œ ์ƒˆ๋กœ์šด ๋„๊ตฌ ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ

์›์น™์ ์œผ๋กœ ํ‘ธ์‹œํ•  ๋•Œ๋งˆ๋‹ค gitlab-runner๋ฅผ ๋‹ค์‹œ ๋ฐฐํฌํ•  ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค. ๋ฌผ๋ก  ๊ตฌ์„ฑ์ด ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์€ ํ•œ ์ˆ˜์ •ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. .gitlab-ci.yml:

deploy_gitlab_runner:
  extends: .deploy_qbec_app
  variables:
    GIT_SUBMODULE_STRATEGY: normal
  before_script:
    - base64 -d "$GITCRYPT_KEY" | git-crypt unlock -
  script:
    - qbec apply default --root deploy/gitlab-runner --force:k8s-context __incluster__ --wait --yes
  only:
    changes:
      - deploy/gitlab-runner/**/*

๋ณ€๊ฒฝ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐฐํฌ/gitlab-runner/ ์žˆ๋Š” ๊ฒฝ์šฐ์—๋งŒ ์ž‘์—…์„ ํŠธ๋ฆฌ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ปค๋ฐ‹ํ•˜๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋งˆ์„ธ์š”.

git add .gitlab-ci.yml
git commit -m "Reduce gitlab-runner deploy"

git push, ๊ทธ๊ฒŒ ๋‚ซ๋‹ค:

์—…๋ฐ์ดํŠธ๋œ ํŒŒ์ดํ”„๋ผ์ธ์˜ ์Šคํฌ๋ฆฐ์ƒท

Kubernetes์—์„œ ๋ฐฐํฌ๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ  ์ž๋™ํ™”ํ•˜๊ธฐ ์œ„ํ•œ ์ƒˆ๋กœ์šด ๋„๊ตฌ ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ

12. ๋™์  ํ™˜๊ฒฝ

์ด์ œ ์—ญ๋™์ ์ธ ํ™˜๊ฒฝ์œผ๋กœ ํŒŒ์ดํ”„๋ผ์ธ์„ ๋‹ค์–‘ํ™”ํ•  ๋•Œ์ž…๋‹ˆ๋‹ค.

๋จผ์ € ์ž‘์—…์„ ์—…๋ฐ์ดํŠธํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. build_website ์šฐ๋ฆฌ์˜ .gitlab-ci.yml, ๋ธ”๋ก์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. ๋งŒ, ์ด๋Š” Gitlab์ด ๋ชจ๋“  ๋ธŒ๋žœ์น˜์— ๋Œ€ํ•œ ์ปค๋ฐ‹์—์„œ ์ด๋ฅผ ํŠธ๋ฆฌ๊ฑฐํ•˜๋„๋ก ๊ฐ•์ œํ•ฉ๋‹ˆ๋‹ค.

build_website:
  extends: .build_docker_image
  variables:
    GIT_SUBMODULE_STRATEGY: normal
  script:
    - mkdir -p $CI_PROJECT_DIR/artifacts
    - /kaniko/executor --cache --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/dockerfiles/website/Dockerfile --destination $CI_REGISTRY_IMAGE/website:$CI_COMMIT_REF_NAME --digest-file $CI_PROJECT_DIR/artifacts/website.digest
  artifacts:
    paths:
      - artifacts/

๊ทธ๋Ÿฐ ๋‹ค์Œ ์ž‘์—…์„ ์—…๋ฐ์ดํŠธํ•˜์„ธ์š”. ๋ฐฐํฌ_์›น์‚ฌ์ดํŠธ, ๊ฑฐ๊ธฐ์— ๋ธ”๋ก์„ ์ถ”๊ฐ€ํ•˜์„ธ์š” ํ™˜๊ฒฝ:

deploy_website:
  extends: .deploy_qbec_app
  environment:
    name: prod
    url: https://docs.example.org
  script:
    - DIGEST="$(cat artifacts/website.digest)"
    - qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST"

์ด๋ฅผ ํ†ตํ•ด Gitlab์€ ์ž‘์—…์„ ๋‹ค์Œ๊ณผ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฐŒ๋ฅด๋‹ค ํ™˜๊ฒฝ์— ๋Œ€ํ•œ ์˜ฌ๋ฐ”๋ฅธ ๋งํฌ๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

์ด์ œ ๋‘ ๊ฐ€์ง€ ์ž‘์—…์„ ๋” ์ถ”๊ฐ€ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

deploy_website:
  extends: .deploy_qbec_app
  environment:
    name: prod
    url: https://docs.example.org
  script:
    - DIGEST="$(cat artifacts/website.digest)"
    - qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST"

deploy_review:
  extends: .deploy_qbec_app
  environment:
    name: review/$CI_COMMIT_REF_NAME
    url: http://$CI_ENVIRONMENT_SLUG.docs.example.org
    on_stop: stop_review
  script:
    - DIGEST="$(cat artifacts/website.digest)"
    - qbec apply review --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST" --vm:ext-str subdomain="$CI_ENVIRONMENT_SLUG" --app-tag "$CI_ENVIRONMENT_SLUG"
  only:
    refs:
    - branches
  except:
    refs:
      - master

stop_review:
  extends: .deploy_qbec_app
  environment:
    name: review/$CI_COMMIT_REF_NAME
    action: stop
  stage: deploy
  before_script:
    - git clone "$CI_REPOSITORY_URL" master
    - cd master
  script:
    - qbec delete review --root deploy/website --force:k8s-context __incluster__ --yes --vm:ext-str digest="$DIGEST" --vm:ext-str subdomain="$CI_ENVIRONMENT_SLUG" --app-tag "$CI_ENVIRONMENT_SLUG"
  variables:
    GIT_STRATEGY: none
  only:
    refs:
    - branches
  except:
    refs:
      - master
  when: manual

๋งˆ์Šคํ„ฐ๋ฅผ ์ œ์™ธํ•œ ๋ชจ๋“  ๋ถ„๊ธฐ์— ํ‘ธ์‹œํ•˜๋ฉด ์‹œ์ž‘๋˜๋ฉฐ ์‚ฌ์ดํŠธ์˜ ๋ฏธ๋ฆฌ ๋ณด๊ธฐ ๋ฒ„์ „์ด ๋ฐฐํฌ๋ฉ๋‹ˆ๋‹ค.

qbec์— ๋Œ€ํ•œ ์ƒˆ๋กœ์šด ์˜ต์…˜์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. --์•ฑ ํƒœ๊ทธ โ€” ๋ฐฐํฌ๋œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฒ„์ „์— ํƒœ๊ทธ๋ฅผ ์ง€์ •ํ•˜๊ณ  ์ด ํƒœ๊ทธ ๋‚ด์—์„œ๋งŒ ์ž‘์—…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Kubernetes์—์„œ ๋ฆฌ์†Œ์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์‚ญ์ œํ•  ๋•Œ qbec์€ ํ•ด๋‹น ๋ฆฌ์†Œ์Šค๋กœ๋งŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.
์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ฐ ๋ฆฌ๋ทฐ๋งˆ๋‹ค ๋ณ„๋„์˜ ํ™˜๊ฒฝ์„ ๋งŒ๋“ค ์ˆ˜ ์—†๊ณ  ๋‹จ์ˆœํžˆ ๋™์ผํ•œ ํ™˜๊ฒฝ์„ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ์šฐ๋ฆฌ๋„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค qbec ์ ์šฉ ๊ฒ€ํ† ๋Œ€์‹ ์— qbec ๊ธฐ๋ณธ๊ฐ’ ์ ์šฉ - ์ด๊ฒƒ์ด ๋ฐ”๋กœ ํ™˜๊ฒฝ(๊ฒ€ํ†  ๋ฐ ๊ธฐ๋ณธ๊ฐ’)์˜ ์ฐจ์ด์ ์„ ์„ค๋ช…ํ•˜๋ ค๊ณ  ์‹œ๋„ํ•˜๋Š” ์ˆœ๊ฐ„์ž…๋‹ˆ๋‹ค.

์ถ”๊ฐ€ ๋ฆฌ๋ทฐ ํ™˜๊ฒฝ ๋ฐฐํฌ/์›น์‚ฌ์ดํŠธ/qbec.yaml

spec:
  environments:
    review:
      defaultNamespace: docs
      server: https://kubernetes.example.org:8443

๊ทธ๋Ÿฌ๋ฉด ์šฐ๋ฆฌ๋Š” ๊ทธ๊ฒƒ์„ ์„ ์–ธํ•  ๊ฒƒ์ด๋‹ค. ๋ฐฐํฌ/์›น์‚ฌ์ดํŠธ/params.libsonnet:

local env = std.extVar('qbec.io/env');
local paramsMap = {
  _: import './environments/base.libsonnet',
  default: import './environments/default.libsonnet',
  review: import './environments/review.libsonnet',
};

if std.objectHas(paramsMap, env) then paramsMap[env] else error 'environment ' + env + ' not defined in ' + std.thisFile

๊ทธ๋ฆฌ๊ณ  ์ด์— ๋Œ€ํ•œ ๋งž์ถค ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ ์–ด๋‘์„ธ์š”. ๋ฐฐํฌ/์›น์‚ฌ์ดํŠธ/ํ™˜๊ฒฝ/review.libsonnet:

// this file has the param overrides for the default environment
local base = import './base.libsonnet';
local slug = std.extVar('qbec.io/tag');
local subdomain = std.extVar('subdomain');

base {
  components+: {
    website+: {
      name: 'example-docs-' + slug,
      domain: subdomain + '.docs.example.org',
    },
  },
}

์กฐ๋ถ€๋„ ์ข€ ๋” ์ž์„ธํžˆ ์‚ดํŽด๋ณด์ž stop_review, ๋ธŒ๋žœ์น˜๊ฐ€ ์‚ญ์ œ๋˜๋ฉด ํŠธ๋ฆฌ๊ฑฐ๋˜๋ฉฐ gitlab์ด ์ฒดํฌ์•„์›ƒ์„ ์‹œ๋„ํ•˜์ง€ ์•Š๋„๋ก ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. GIT_STRATEGY: ์—†์Œ, ๋‚˜์ค‘์— ๋ณต์ œํ•ฉ๋‹ˆ๋‹ค ์„์‚ฌ-๋ธŒ๋žœ์น˜๋ฅผ ํ†ตํ•ด ๋ฆฌ๋ทฐ๋ฅผ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.
์กฐ๊ธˆ ํ—ท๊ฐˆ๋ฆฌ๊ธด ํ•˜์ง€๋งŒ ์•„์ง์€ ์ด๋ณด๋‹ค ๋” ์•„๋ฆ„๋‹ค์šด ๋ฐฉ๋ฒ•์„ ์ฐพ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.
๋˜ ๋‹ค๋ฅธ ์˜ต์…˜์€ ๊ฐ ๋ฆฌ๋ทฐ๋ฅผ ํ˜ธํ…” ๋„ค์ž„์ŠคํŽ˜์ด์Šค์— ๋ฐฐํฌํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋Š” ํ•ญ์ƒ ์™„์ „ํžˆ ์ฒ ๊ฑฐ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ปค๋ฐ‹ํ•˜๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋งˆ์„ธ์š”.

git add .
git commit -m "Enable automatic review"

git push, ์ž์‹ ์ฒดํฌ ์•„์›ƒ -b ํ…Œ์ŠคํŠธ, git ํ‘ธ์‹œ ์˜ค๋ฆฌ์ง„ ํ…Œ์ŠคํŠธ, ํ™•์ธํ•˜๋‹ค:

Gitlab์—์„œ ์ƒ์„ฑ๋œ ํ™˜๊ฒฝ์˜ ์Šคํฌ๋ฆฐ์ƒท

Kubernetes์—์„œ ๋ฐฐํฌ๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ  ์ž๋™ํ™”ํ•˜๊ธฐ ์œ„ํ•œ ์ƒˆ๋กœ์šด ๋„๊ตฌ ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ

๋ชจ๋“  ๊ฒƒ์ด ์ž‘๋™ํ•˜๊ณ  ์žˆ๋‚˜์š”? - ์ข‹์Šต๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ ๋ธŒ๋žœ์น˜๋ฅผ ์‚ญ์ œํ•˜์„ธ์š”. ์ž์‹ ์ฒดํฌ ์•„์›ƒ ๋งˆ์Šคํ„ฐ, git push ์›์  : ํ…Œ์ŠคํŠธ, ํ™˜๊ฒฝ ์‚ญ์ œ ์ž‘์—…์ด ์˜ค๋ฅ˜ ์—†์ด ์ž‘๋™ํ–ˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ๋Š” ํ”„๋กœ์ ํŠธ์˜ ๋ชจ๋“  ๊ฐœ๋ฐœ์ž๊ฐ€ ๋ธŒ๋žœ์น˜๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๊ณ  ๋ณ€๊ฒฝํ•  ์ˆ˜๋„ ์žˆ๋‹ค๋Š” ์ ์„ ์ฆ‰์‹œ ๋ช…ํ™•ํžˆ ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. .gitlab-ci.yml ํŒŒ์ผ์„ ์ €์žฅํ•˜๊ณ  ๋น„๋ฐ€ ๋ณ€์ˆ˜์— ์•ก์„ธ์Šคํ•ฉ๋‹ˆ๋‹ค.
๋”ฐ๋ผ์„œ ๋ณดํ˜ธ๋œ ๋ถ„๊ธฐ์—๋งŒ ์‚ฌ์šฉ์„ ํ—ˆ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์„์‚ฌ, ๋˜๋Š” ๊ฐ ํ™˜๊ฒฝ์— ๋Œ€ํ•ด ๋ณ„๋„์˜ ๋ณ€์ˆ˜ ์„ธํŠธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

13. ์•ฑ ๊ฒ€ํ† 

์•ฑ ๊ฒ€ํ†  ์ €์žฅ์†Œ์˜ ๊ฐ ํŒŒ์ผ์— ๋ฒ„ํŠผ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ๋ฐฐํฌ๋œ ํ™˜๊ฒฝ์—์„œ ๋น ๋ฅด๊ฒŒ ๋ณผ ์ˆ˜ ์žˆ๋Š” GitLab ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

์ด ๋ฒ„ํŠผ์„ ํ‘œ์‹œํ•˜๋ ค๋ฉด ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. .gitlab/route-map.yml ๊ทธ๋ฆฌ๊ณ  ๊ทธ ์•ˆ์˜ ๋ชจ๋“  ๊ฒฝ๋กœ ๋ณ€ํ™˜์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ์˜ ๊ฒฝ์šฐ์—๋Š” ๋งค์šฐ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค.

# Indices
- source: /content/(.+?)_index.(md|html)/ 
  public: '1'

# Pages
- source: /content/(.+?).(md|html)/ 
  public: '1/'

๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ปค๋ฐ‹ํ•˜๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋งˆ์„ธ์š”.

git add .gitlab/
git commit -m "Enable review apps"

git push, ํ™•์ธ:

์•ฑ ๊ฒ€ํ†  ๋ฒ„ํŠผ ์Šคํฌ๋ฆฐ์ƒท

Kubernetes์—์„œ ๋ฐฐํฌ๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ  ์ž๋™ํ™”ํ•˜๊ธฐ ์œ„ํ•œ ์ƒˆ๋กœ์šด ๋„๊ตฌ ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ

์ž‘์—…์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!

ํ”„๋กœ์ ํŠธ ์†Œ์Šค:

๊ด€์‹ฌ์„ ๊ฐ€์ ธ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๋งˆ์Œ์— ๋“œ์…จ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค. Kubernetes์—์„œ ๋ฐฐํฌ๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ  ์ž๋™ํ™”ํ•˜๊ธฐ ์œ„ํ•œ ์ƒˆ๋กœ์šด ๋„๊ตฌ ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ

์ถœ์ฒ˜ : habr.com

์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ถ”๊ฐ€