<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>움직이는 월e</title>
    <link>https://narakit.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sat, 25 Jul 2026 14:00:41 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>로파이</managingEditor>
    <image>
      <title>움직이는 월e</title>
      <url>https://tistory1.daumcdn.net/tistory/4430887/attach/2d8066d665b74cd3bec96a486889ac1b</url>
      <link>https://narakit.tistory.com</link>
    </image>
    <item>
      <title>[CryptoPP] SRP 프로토콜 (Secure Remote Password Protocol)</title>
      <link>https://narakit.tistory.com/321</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트- 서버 모델에서 유저 이름 (username)과 비밀번호 (password)를 기반으로 인증된 키를 교환하는 프로토콜. (PAKE)&lt;br /&gt;&lt;br /&gt;간단히 요약하자면 Client 와 Server가 username, password를 알고 있을 때, 공개 키만을 교환하여 동일한 비공개 키를 각 진영에서 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;절차&lt;/b&gt;&lt;br /&gt;공통 알고 있는 사항 username (I), password (p)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;H (A,...,Z) = 해시 알고리즘 (ex SHA-512) =&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A, ... , Z 까지 숫자에 대한 해시&lt;br /&gt;&lt;br /&gt;N = 충분히 큰 소수&lt;br /&gt;g = 2 ; g ^ x mod N group을 만들어내는 generator&lt;br /&gt;k = H (M, g) ; SRP 파라미터&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;1. 유저 DB 생성 (서버)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저 s = salt, 특정 길이의 랜덤 문자열을 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비밀 키 x = H (s, I, p);&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패스워드 검증기 v = pow (g, x, N); 비밀 번호를 직접 들고 있지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;2. 공개 키 교환&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client, Server에 각각 a, b는 public key를 만들기 위한 salt 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client -&amp;gt; Server : username, client_public_key (A)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server -&amp;gt; Client : server_public_key (B), user_salt (s)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 세션 키 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client 는 Server 로 부터 받은 salt를 이용하여 비밀 키를 생성&lt;br /&gt;x = H(s, I, p)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 공개 키 B 를 조합하여 세션 키를 생성&lt;br /&gt;Client의 세션 키 = F_c ( a, B, x, N, k)&amp;nbsp;&lt;br /&gt;&lt;br /&gt;Server는 클라이언트 공개키 A를 이용하여 세션키를 생성&lt;br /&gt;Server의 세션 키 = F_s (A, b, v, N)&lt;br /&gt;&lt;br /&gt;&lt;i&gt;&lt;b&gt;이 때, Client 세션 키와 Server 세션 키는 같아야한다.&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로 Client 쪽에서 client_proof_key를 Server에 제공하여 Server가 Client를 검증할 수 있고&lt;br /&gt;Server가 server_proof_key를 Client에 제공하여 Client가 Server를 검증할 수 있다.&lt;br /&gt;&lt;br /&gt;SPR 예제&lt;br /&gt;&lt;br /&gt;SRP.h&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1756535696343&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &amp;lt;string&amp;gt;
#include &amp;lt;memory&amp;gt;

namespace Crypto {
	class SRP final {
	public:
		class Client;
		class Server;

		SRP(const std::string_view&amp;amp; username, const std::string_view&amp;amp; password);
		SRP(const std::string_view&amp;amp; username, const std::string_view&amp;amp; password, const std::string_view&amp;amp; user_salt);
		~SRP();

		static void Initialize(); // Initialize SRP parameters (N, g)

		Client GetClient();
		Server GetServer();

	private:
		class Impl;
		std::unique_ptr&amp;lt;Impl&amp;gt; impl_;

	public:
		class Client {
		public:
			Client(Impl* impl);
			~Client();

			std::string GetPublicKey();
			bool CalculateSessionKey(const std::string_view&amp;amp; server_public_key);
			const std::string&amp;amp; GetSessionKey() const { return session_key_; }

			std::string CalculateClientProof(const std::string_view&amp;amp; server_public_key);
			bool VerifyServerProof(const std::string_view&amp;amp; server_public_key, const std::string_view&amp;amp; server_proof);

		private:
			Impl* impl_;
			std::string a_hex_;
			std::string A_hex_;
			std::string session_key_;
		};

		class Server {
		public:
			Server(Impl* impl);
			~Server();

			std::string GetPublicKey() const;
			std::string GetUserSalt() const;

			bool CalculateSessionKey(const std::string_view&amp;amp; client_public_key);
			const std::string&amp;amp; GetSessionKey() const { return session_key_; }

			std::string CalculateServerProof(const std::string_view&amp;amp; client_public_key, const std::string_view&amp;amp; client_proof);
			bool VerifyClientProof(const std::string_view&amp;amp; client_public_key, const std::string_view&amp;amp; client_proof);

		private:
			Impl* impl_;
			std::string b_hex_;
			std::string B_hex_;
			std::string session_key_;
		};
	};
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;SRP.cpp&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1756535739021&quot; class=&quot;arduino&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;#include &quot;stdafx.h&quot;
#include &quot;SRP.h&quot;
#include &quot;Core/Crypto/Hex.h&quot;
#include &quot;Core/Crypto/Salt.h&quot;
#include &quot;Core/Crypto/Hash/SHA512.h&quot;
#include &quot;Core/Crypto/Internal/Prime.h&quot;
#include &amp;lt;cryptopp/integer.h&amp;gt;
#include &amp;lt;cryptopp/nbtheory.h&amp;gt;
#include &amp;lt;cryptopp/modarith.h&amp;gt;
#include &amp;lt;cryptopp/osrng.h&amp;gt;

namespace Crypto {
	class SRPParam : public System::Singleton&amp;lt;SRPParam&amp;gt; {
	public:
		SRPParam(Protection) {
			GeneratePrime(2048, 1024, &amp;amp;N);
		}

		CryptoPP::Integer N;
		CryptoPP::Integer g = 2;
	};

	class SRP::Impl {
	public:
		Impl(const std::string_view&amp;amp; username, const std::string_view&amp;amp; password) 
			:
			username_(username),
			salt_hex_(ToHex(Salt(16))) {
			const CryptoPP::Integer* SRP_N = &amp;amp;SRPParam::GetInstance().N;
			const CryptoPP::Integer* SRP_g = &amp;amp;SRPParam::GetInstance().g;

			// k = H(N | g)
			std::string k_source = FORMAT(&quot;{}:{}&quot;, ToHex(*SRP_N), ToHex(*SRP_g));
			std::string k_hash = SHA512::Compute(k_source);
			std::string k_hex = ToHex(k_hash);
			k_ = CryptoPP::Integer(k_hex.c_str());

			std::string private_key_source = FORMAT(&quot;{}:{}:{}&quot;, salt_hex_, username, password);
			std::string private_key_hash = SHA512::Compute(private_key_source);
			std::string private_key_hex = ToHex(private_key_hash);

			x_ = ToInteger(private_key_hex);
			verfier_ = CryptoPP::ModularExponentiation(CryptoPP::Integer(2), x_, *SRP_N);
		}

		Impl(const std::string_view&amp;amp; username, const std::string_view&amp;amp; password, const std::string_view&amp;amp; user_salt)
			:
			username_(username),
			salt_hex_(user_salt) {
			const CryptoPP::Integer* SRP_N = &amp;amp;SRPParam::GetInstance().N;
			const CryptoPP::Integer* SRP_g = &amp;amp;SRPParam::GetInstance().g;
			// k = H(N | g)
			std::string k_source = FORMAT(&quot;{}:{}&quot;, ToHex(*SRP_N), ToHex(*SRP_g));
			std::string k_hash = SHA512::Compute(k_source);
			std::string k_hex = ToHex(k_hash);
			k_ = CryptoPP::Integer(k_hex.c_str());
			std::string private_key_source = FORMAT(&quot;{}:{}:{}&quot;, salt_hex_, username, password);
			std::string private_key_hash = SHA512::Compute(private_key_source);
			std::string private_key_hex = ToHex(private_key_hash);
			x_ = ToInteger(private_key_hex);
			verfier_ = CryptoPP::ModularExponentiation(CryptoPP::Integer(2), x_, *SRP_N);
		}

		~Impl() {
		}

	private:
		friend class SRP::Server;
		friend class SRP::Client;

		std::string username_;
		CryptoPP::Integer k_; // k = H(N | g)
		CryptoPP::Integer x_; // x = H(s | H(I | &quot;:&quot; | P))
		CryptoPP::Integer verfier_; // v = g^x % N
		std::string salt_hex_;
	};

	SRP::SRP(const std::string_view&amp;amp; username, const std::string_view&amp;amp; password)
		:
		impl_(std::make_unique&amp;lt;Impl&amp;gt;(username, password))  {

	}

	SRP::SRP(const std::string_view&amp;amp; username, const std::string_view&amp;amp; password, const std::string_view&amp;amp; user_salt)
		:
		impl_(std::make_unique&amp;lt;Impl&amp;gt;(username, password, user_salt))
	{
	}

	SRP::~SRP() = default;

	void SRP::Initialize() {
		SRPParam::GetInstance();
	}

	SRP::Client SRP::GetClient() {
		return SRP::Client(impl_.get());
	}

	SRP::Server SRP::GetServer() {
		return Server(impl_.get());
	}

	SRP::Client::Client(Impl* impl)
		:
		impl_(impl) {
		const CryptoPP::Integer* SRP_N = &amp;amp;SRPParam::GetInstance().N;
		const CryptoPP::Integer* SRP_g = &amp;amp;SRPParam::GetInstance().g;

		a_hex_ = ToHex(Salt(16));
		CryptoPP::Integer a = ToInteger(a_hex_.c_str());
		A_hex_ = ToHex(CryptoPP::ModularExponentiation(*SRP_g, a, *SRP_N));
	}

	SRP::Client::~Client() = default;

	std::string SRP::Client::GetPublicKey() {
		return A_hex_;
	}

	bool SRP::Client::CalculateSessionKey(const std::string_view&amp;amp; server_public_key/*B_hex*/) {
		const CryptoPP::Integer* SRP_N = &amp;amp;SRPParam::GetInstance().N;

		CryptoPP::Integer A = ToInteger(A_hex_);
		CryptoPP::Integer B = ToInteger(server_public_key);

		if (B % (*SRP_N) == 0) {
			return false;
		}

		std::string u_hex = SHA512::Compute(FORMAT(&quot;{}:{}&quot;, A_hex_, server_public_key));

		CryptoPP::Integer u = ToInteger(u_hex);
		CryptoPP::Integer a = ToInteger(a_hex_);

		CryptoPP::Integer exp = (a + u * impl_-&amp;gt;x_);
		CryptoPP::Integer base = (B - impl_-&amp;gt;k_ * impl_-&amp;gt;verfier_);
		CryptoPP::Integer S = CryptoPP::ModularExponentiation(base, exp, *SRP_N);
		session_key_ = ToHex(SHA512::Compute(ToHex(S)));

		return true;
	}

	SRP::Server::Server(Impl* impl)
		:
		impl_(impl) {
		const CryptoPP::Integer* SRP_N = &amp;amp;SRPParam::GetInstance().N;
		const CryptoPP::Integer* SRP_g = &amp;amp;SRPParam::GetInstance().g;
		b_hex_ = ToHex(Salt(16));
		CryptoPP::Integer b = ToInteger(b_hex_);
		CryptoPP::Integer gb = CryptoPP::ModularExponentiation(*SRP_g, b, *SRP_N);
		CryptoPP::Integer k_mul_v = (impl_-&amp;gt;k_ * impl_-&amp;gt;verfier_);
		CryptoPP::Integer B = (k_mul_v + gb) % (*SRP_N);
		B_hex_ = ToHex(B);
	}

	SRP::Server::~Server() = default;

	std::string SRP::Server::GetPublicKey() const {
		return B_hex_;
	}

	std::string SRP::Server::GetUserSalt() const { 
		return impl_-&amp;gt;salt_hex_; 
	}

	bool SRP::Server::CalculateSessionKey(const std::string_view&amp;amp; client_public_key/*A_hex*/) {
		const CryptoPP::Integer* SRP_N = &amp;amp;SRPParam::GetInstance().N;

		std::string u_hex = SHA512::Compute(FORMAT(&quot;{}:{}&quot;, client_public_key, B_hex_));
		CryptoPP::Integer A = ToInteger(client_public_key);
		CryptoPP::Integer B = ToInteger(B_hex_);
		CryptoPP::Integer u = ToInteger(u_hex);
		if (A % (*SRP_N) == 0) {
			return false;
		}

		CryptoPP::Integer exp = ToInteger(b_hex_);
		CryptoPP::Integer base = (A * CryptoPP::ModularExponentiation(impl_-&amp;gt;verfier_, u, *SRP_N));
		CryptoPP::Integer S = CryptoPP::ModularExponentiation(base, exp, *SRP_N);
		session_key_ = ToHex(SHA512::Compute(ToHex(S)));

		return true;
	}

	std::string SRP::Client::CalculateClientProof(const std::string_view&amp;amp; server_public_key/*B_hex*/) {
		// M1 = H(A | B | S_client)
		return SHA512::Compute(FORMAT(&quot;{}:{}:{}&quot;, A_hex_, server_public_key, session_key_));
	}

	bool SRP::Client::VerifyServerProof(const std::string_view&amp;amp; server_public_key/*B_hex*/, const std::string_view&amp;amp; server_proof) {
		// M2 = H(A | M1 | S_server)
		std::string M1 = CalculateClientProof(server_public_key);
		std::string expected_server_proof = SHA512::Compute(FORMAT(&quot;{}:{}:{}&quot;, A_hex_, M1, session_key_));
		return expected_server_proof == server_proof;
	}

	std::string SRP::Server::CalculateServerProof(const std::string_view&amp;amp; client_public_key/*A_hex*/, const std::string_view&amp;amp; client_proof) {
		// M2 = H(A | M1 | S_server)
		return SHA512::Compute(FORMAT(&quot;{}:{}:{}&quot;, client_public_key, client_proof, session_key_));
	}

	bool SRP::Server::VerifyClientProof(const std::string_view&amp;amp; client_public_key/*A_hex*/, const std::string_view&amp;amp; client_proof) {
		// M1 = H(A | B | S_client)
		std::string expected_client_proof = SHA512::Compute(FORMAT(&quot;{}:{}:{}&quot;, client_public_key, B_hex_, session_key_));
		return expected_client_proof == client_proof;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;&lt;br /&gt;유닛 테스트로 알아보는 절차&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1756535860700&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;TEST_METHOD(SRPTest)
{
	Crypto::SRP srp_server(&quot;username&quot;, &quot;password&quot;);
	auto server = srp_server.GetServer();

	Crypto::SRP srp_client(&quot;username&quot;, &quot;password&quot;, server.GetUserSalt());
	auto client = srp_client.GetClient();

	std::string client_public_key = client.GetPublicKey();
	std::string server_public_key = server.GetPublicKey();

	bool client_session_ok = client.CalculateSessionKey(server_public_key);
	bool server_session_ok = server.CalculateSessionKey(client_public_key);

	Assert::IsTrue(client_session_ok);
	Assert::IsTrue(server_session_ok);

	std::string client_session_key = client.GetSessionKey();
	std::string server_session_key = server.GetSessionKey();

	Assert::AreEqual(client_session_key, server_session_key);

	std::string client_proof = client.CalculateClientProof(server_public_key);
	bool server_proof_ok = server.VerifyClientProof(client_public_key, client_proof);
	std::string server_proof = server.CalculateServerProof(client_public_key, client_proof);
	bool client_proof_ok = client.VerifyServerProof(server_public_key, server_proof);

	Assert::IsTrue(client_proof_ok);
	Assert::IsTrue(server_proof_ok);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;&lt;br /&gt;&lt;br /&gt;참고:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;background-color: #fafafa; color: #0070d1; text-align: start;&quot; href=&quot;https://en.wikipedia.org/wiki/Secure_Remote_Password_protocol&quot;&gt;Secure Remote Password protocol - Wikipedia&lt;/a&gt;&lt;/p&gt;</description>
      <category>C++/암호학</category>
      <author>로파이</author>
      <guid isPermaLink="true">https://narakit.tistory.com/321</guid>
      <comments>https://narakit.tistory.com/321#entry321comment</comments>
      <pubDate>Sat, 30 Aug 2025 15:35:12 +0900</pubDate>
    </item>
    <item>
      <title>[CryptoPP] SHA hash</title>
      <link>https://narakit.tistory.com/320</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;임의의 바이트 열에 대하여 해시를 계산할 수 있는 알고리즘&lt;br /&gt;&lt;br /&gt;미국 국가 안보국 NSA 에서 설계 하였으며, SHA-1, SHA-2 가 차례로 개발됌.&lt;br /&gt;SHA-2 그룹 중 SHA-256/512 해시 알고리즘을 많이 사용하며 뒤에 붙은 숫자는 다이제스트 (결과 바이트 수)를 만들어내는 해시 함수를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) SHA-256은 4바이트 * 8 (32 * 8), SHA-512의 경우 8바이트 * 8 (64 * 8)&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Davies-Meyer_hash.svg.png&quot; data-origin-width=&quot;250&quot; data-origin-height=&quot;279&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMqgMi/btsQcHYbGLc/jmkwDU1PN4xUkIo7elSLkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMqgMi/btsQcHYbGLc/jmkwDU1PN4xUkIo7elSLkK/img.png&quot; data-alt=&quot;단방향 압축 기능 - Wikipedia&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMqgMi/btsQcHYbGLc/jmkwDU1PN4xUkIo7elSLkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMqgMi%2FbtsQcHYbGLc%2FjmkwDU1PN4xUkIo7elSLkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;139&quot; height=&quot;155&quot; data-filename=&quot;Davies-Meyer_hash.svg.png&quot; data-origin-width=&quot;250&quot; data-origin-height=&quot;279&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;단방향 압축 기능 - Wikipedia&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이비스 - 마이어 Davies-Meyer의 단 방향 압축 함수 PlainText (H_i-1) + Key(m_i) -&amp;gt; CipherText (Hi) 를 만들어내는 구조가 기본 바탕이며 Merkle-Damgard 구성 을 더해 충돌 방지 암호화 해시 함수를 설계되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성능&lt;br /&gt;원본 바이트를 추적하기 어렵게 하는 역상 저항성 (preimage resistance)과 두 plaintext의 해시 충돌이 적도록 하는 충돌 저항성 (collision resistance)에 대해 실용적인 보안 수준을 제공&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[by gpt]&lt;br /&gt;&lt;br /&gt;1.&amp;nbsp;Preimage&amp;nbsp;Resistance&amp;nbsp;(역상&amp;nbsp;저항성) &lt;br /&gt;&amp;bull; 정의:&amp;nbsp;주어진&amp;nbsp;해시&amp;nbsp;값&amp;nbsp;H에&amp;nbsp;대해,&amp;nbsp;원래&amp;nbsp;입력값&amp;nbsp;M을&amp;nbsp;찾는&amp;nbsp;것이&amp;nbsp;계산적으로&amp;nbsp;어려운&amp;nbsp;성질입니다.&amp;nbsp;즉,&amp;nbsp;H&amp;nbsp;=&amp;nbsp;Hash(M)인&amp;nbsp;M을&amp;nbsp;찾는&amp;nbsp;것이&amp;nbsp;매우&amp;nbsp;어렵다는&amp;nbsp;것을&amp;nbsp;의미합니다. &lt;br /&gt;&amp;bull; 문장에서의&amp;nbsp;의미: &lt;br /&gt;&amp;bull; SHA-256의&amp;nbsp;경우,&amp;nbsp;64라운드&amp;nbsp;중&amp;nbsp;52라운드에&amp;nbsp;대해&amp;nbsp;역상&amp;nbsp;저항성을&amp;nbsp;깨는&amp;nbsp;공격이&amp;nbsp;존재합니다. &lt;br /&gt;&amp;bull; SHA-512의&amp;nbsp;경우,&amp;nbsp;80라운드&amp;nbsp;중&amp;nbsp;57라운드에&amp;nbsp;대해&amp;nbsp;역상&amp;nbsp;저항성을&amp;nbsp;깨는&amp;nbsp;공격이&amp;nbsp;존재합니다. &lt;br /&gt;&amp;bull; 이는&amp;nbsp;전체&amp;nbsp;라운드&amp;nbsp;수보다&amp;nbsp;적은&amp;nbsp;라운드에서만&amp;nbsp;공격이&amp;nbsp;가능하다는&amp;nbsp;것을&amp;nbsp;의미하며,&amp;nbsp;전체&amp;nbsp;라운드에서는&amp;nbsp;여전히&amp;nbsp;안전하다는&amp;nbsp;것을&amp;nbsp;암시합니다. &lt;br /&gt;--- &lt;br /&gt;2.&amp;nbsp;Collision&amp;nbsp;Resistance&amp;nbsp;(충돌&amp;nbsp;저항성) &lt;br /&gt;&amp;bull; 정의:&amp;nbsp;서로&amp;nbsp;다른&amp;nbsp;두&amp;nbsp;입력값&amp;nbsp;M1과&amp;nbsp;M2에&amp;nbsp;대해,&amp;nbsp;동일한&amp;nbsp;해시&amp;nbsp;값&amp;nbsp;H를&amp;nbsp;생성하는&amp;nbsp;것이&amp;nbsp;계산적으로&amp;nbsp;어려운&amp;nbsp;성질입니다.&amp;nbsp;즉,&amp;nbsp;Hash(M1)&amp;nbsp;=&amp;nbsp;Hash(M2)인&amp;nbsp;M1과&amp;nbsp;M2를&amp;nbsp;찾는&amp;nbsp;것이&amp;nbsp;매우&amp;nbsp;어렵다는&amp;nbsp;것을&amp;nbsp;의미합니다. &lt;br /&gt;&amp;bull; 문장에서의&amp;nbsp;의미: &lt;br /&gt;&amp;bull; SHA-256의&amp;nbsp;경우,&amp;nbsp;64라운드&amp;nbsp;중&amp;nbsp;46라운드에&amp;nbsp;대해&amp;nbsp;충돌&amp;nbsp;저항성을&amp;nbsp;깨는&amp;nbsp;공격이&amp;nbsp;존재합니다. &lt;br /&gt;&amp;bull; 이는&amp;nbsp;전체&amp;nbsp;라운드&amp;nbsp;수보다&amp;nbsp;적은&amp;nbsp;라운드에서만&amp;nbsp;충돌&amp;nbsp;저항성을&amp;nbsp;깨는&amp;nbsp;것이&amp;nbsp;가능하며,&amp;nbsp;전체&amp;nbsp;라운드에서는&amp;nbsp;여전히&amp;nbsp;안전하다는&amp;nbsp;것을&amp;nbsp;암시합니다. &lt;br /&gt;--- &lt;br /&gt;3.&amp;nbsp;공격의&amp;nbsp;의미 &lt;br /&gt;&amp;bull; &quot;공격이&amp;nbsp;가능하다&quot;는&amp;nbsp;의미: &lt;br /&gt;&amp;bull; 특정&amp;nbsp;라운드&amp;nbsp;수에서&amp;nbsp;공격이&amp;nbsp;가능하다는&amp;nbsp;것은,&amp;nbsp;이&amp;nbsp;라운드&amp;nbsp;수에서&amp;nbsp;해시&amp;nbsp;알고리즘의&amp;nbsp;보안&amp;nbsp;강도가&amp;nbsp;약화된다는&amp;nbsp;것을&amp;nbsp;의미합니다. &lt;br /&gt;&amp;bull; 그러나&amp;nbsp;전체&amp;nbsp;라운드&amp;nbsp;수에서&amp;nbsp;공격이&amp;nbsp;불가능하다면,&amp;nbsp;해당&amp;nbsp;해시&amp;nbsp;알고리즘은&amp;nbsp;여전히&amp;nbsp;안전하다고&amp;nbsp;간주됩니다. &lt;br /&gt;&amp;bull; 공격의&amp;nbsp;실제&amp;nbsp;영향: &lt;br /&gt;&amp;bull; 이&amp;nbsp;공격들은&amp;nbsp;이론적인&amp;nbsp;연구&amp;nbsp;결과에&amp;nbsp;기반하며,&amp;nbsp;실제로는&amp;nbsp;매우&amp;nbsp;높은&amp;nbsp;계산&amp;nbsp;비용이&amp;nbsp;필요합니다. &lt;br /&gt;&amp;bull; 따라서,&amp;nbsp;SHA-256과&amp;nbsp;SHA-512는&amp;nbsp;여전히&amp;nbsp;실용적인&amp;nbsp;보안&amp;nbsp;수준을&amp;nbsp;제공합니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;CryptoPP 간단한 예제&lt;/p&gt;
&lt;pre id=&quot;code_1756533283340&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;cryptopp/sha.h&amp;gt;
#include &amp;lt;cryptopp/hex.h&amp;gt;
#include &amp;lt;cryptopp/filters.h&amp;gt;

std::string SHA512::Compute(const std::string_view&amp;amp; source) {
	std::string digest;
	CryptoPP::SHA512 hash;
	CryptoPP::StringSource(source.data(), true,
		new CryptoPP::HashFilter(hash,
			new CryptoPP::HexEncoder(
				new CryptoPP::StringSink(digest))));
	return digest;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>C++/암호학</category>
      <author>로파이</author>
      <guid isPermaLink="true">https://narakit.tistory.com/320</guid>
      <comments>https://narakit.tistory.com/320#entry320comment</comments>
      <pubDate>Sat, 30 Aug 2025 14:55:04 +0900</pubDate>
    </item>
    <item>
      <title>[웹 서비스]  OAuth 2.0</title>
      <link>https://narakit.tistory.com/318</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;OAuth 2.0&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 웹 서비스 계정을 이용하여 해당 서비스 권한을 간접적으로 획득하여 나의 웹 서버에서 활용하고자 할 때,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 웹 서비스에 나의 웹 서버를 인증하는 프로토콜&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 서비스가 유저의 구글, 야후, 카카오 등 대표 가능한 웹 서비스의 이메일 주소를 이용하고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저의 이메일 주소를 알 수 있다면 나의 웹 사이트에서는 따로 이메일 가입을 할 필요 없이 해당 외부 웹 서비스 계정 정보를 이용하여 유저를 특정하고 계정으로 사용할 수 있을 것 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;참여자&lt;/span&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1519&quot; data-origin-height=&quot;242&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uvhhj/btsLBjy7zNv/R281wJ1KFK4NXNEwSKk8TK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uvhhj/btsLBjy7zNv/R281wJ1KFK4NXNEwSKk8TK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uvhhj/btsLBjy7zNv/R281wJ1KFK4NXNEwSKk8TK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fuvhhj%2FbtsLBjy7zNv%2FR281wJ1KFK4NXNEwSKk8TK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1519&quot; height=&quot;242&quot; data-origin-width=&quot;1519&quot; data-origin-height=&quot;242&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스를 이용하고자 하는 유저 (resource owner) : 내 서비스가 참조하고자 하는 자원 (이메일 주소)를 소유한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 서비스 (client) : 유저의 정보를 활용하고자하는 주체. 웹 서비스가 될 수도 있고 앱 어플리케이션 자체가 요구할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증 서버 (authorization server) : 내 서비스를 등록하고 유저의 요청이 왔을 때, 나의 서비스에 의해 요청된 것인 지 인증하고 나의 서비스에 접근 토큰(access token)을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자원 서버 (resource server): 외부 서비스 중 실제 유저의 정보를 저장하고 있는 서버이다. 나의 서비스는 제공 받은 접근 토큰을 사용해 해당 서버에 직접 API를 요청한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;간단한 절차&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 서비스 등록&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1519&quot; data-origin-height=&quot;584&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KeRfs/btsLBg3IZl2/TTqJtxqfbxo6NTUlyloIqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KeRfs/btsLBg3IZl2/TTqJtxqfbxo6NTUlyloIqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KeRfs/btsLBg3IZl2/TTqJtxqfbxo6NTUlyloIqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKeRfs%2FbtsLBg3IZl2%2FTTqJtxqfbxo6NTUlyloIqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1519&quot; height=&quot;584&quot; data-origin-width=&quot;1519&quot; data-origin-height=&quot;584&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OAuth2.0을 사용하여 인증을 하기 위해 인증 서비스에 내 서비스를 등록한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 서비스에는 유저가 계정 인증을 완료했을 때, 획득한 접근 토큰을 가지고 호출할 콜백 주소 redirect-uri를 등록한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 서비스는 내 서비스를 등록하고 인증 서버의 클라이언트로서 사용할 ClientID와 Client Secret을 제공받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 외부 인증 서버로의 ID와 패스워드 역할을 하고 내 서비스는 이를 config 등으로 관리해둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시) Google Auth&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;848&quot; data-origin-height=&quot;794&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBewZq/btsLBXoP0iQ/VAk0XI8JXh7q6W8UevJGfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBewZq/btsLBXoP0iQ/VAk0XI8JXh7q6W8UevJGfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBewZq/btsLBXoP0iQ/VAk0XI8JXh7q6W8UevJGfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBewZq%2FbtsLBXoP0iQ%2FVAk0XI8JXh7q6W8UevJGfk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;613&quot; height=&quot;574&quot; data-origin-width=&quot;848&quot; data-origin-height=&quot;794&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글에 내 서비스를 등록하면, ClientID와 Secret을 확인할 수 있다. Secret은 응답 내용 등에서 유저에 직접 노출되지 않도록 주의한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;783&quot; data-origin-height=&quot;291&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ELdES/btsLASBZS34/R5bt2jx9JcpkVJnxzPhaP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ELdES/btsLASBZS34/R5bt2jx9JcpkVJnxzPhaP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ELdES/btsLASBZS34/R5bt2jx9JcpkVJnxzPhaP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FELdES%2FbtsLASBZS34%2FR5bt2jx9JcpkVJnxzPhaP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;724&quot; height=&quot;269&quot; data-origin-width=&quot;783&quot; data-origin-height=&quot;291&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 인증 코드 요청&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1519&quot; data-origin-height=&quot;885&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvEFJS/btsLAtJgcTC/sVcWPyZG2V8TICgrF1IIhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvEFJS/btsLAtJgcTC/sVcWPyZG2V8TICgrF1IIhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvEFJS/btsLAtJgcTC/sVcWPyZG2V8TICgrF1IIhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvEFJS%2FbtsLAtJgcTC%2FsVcWPyZG2V8TICgrF1IIhk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1519&quot; height=&quot;885&quot; data-origin-width=&quot;1519&quot; data-origin-height=&quot;885&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이제 라이브 중 인 내 서비스가 유저 외부 계정 정보를 이용하고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저는 외부 서비스를 통한 자신의 정보를 제공하기 위해 내 서비스로 OAuthURL를 요구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 서비스는 유저에게 외부 서비스 계정 로그인 웹 페이지로 연결되는 OAuthURL 주소를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 떄 내 서비스는 &quot;scope&quot; 허락 받을 유저의 정보에 대한 범위를 의미하는 scope를 지정해서 OAuthURL을 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;scope는 이메일 주소, 캘린더, 프로필 등 자원에 대한 접근 범위를 지정한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 서비스라면 OAuthURL로 리다이렉션을 하여 로그인 페이지가 바로 보이게 하거나 앱이라면 Provider의 웹 서비스 앱을 열어 리다이렉션이 될 수도 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;235&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eg9v0E/btsLzvAOCfW/7dLTQqb4kJlbg1Jo5nqJwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eg9v0E/btsLzvAOCfW/7dLTQqb4kJlbg1Jo5nqJwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eg9v0E/btsLzvAOCfW/7dLTQqb4kJlbg1Jo5nqJwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feg9v0E%2FbtsLzvAOCfW%2F7dLTQqb4kJlbg1Jo5nqJwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;920&quot; height=&quot;235&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;235&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 유저가 요청하는 주소의 형식은 대략 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1735374927478&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;https://${auth_server_address}?&amp;amp;client_id=${clientID}&amp;amp;redirect_uri=${redirectURL}?scope=${scope}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저가 인증 서버를 통해 계정 인증을 완료하면, 인증 서버는 해당 redirect_uri와 client_id가 등록된 서비스인 지 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저가 요청한 정보와 내 서비스가 등록한 정보가 일치하지 않는다면, http code가 200이 아닌 에러 응답을 보낼 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증 서버를 통해 정상 인증이 완료되면, 302 코드에 의해 Redirect이 다시 일어나며 응답 헤더에 적힌 Location으로 리다리에션된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때, 요청 주소에 인증 토큰 (인증 코드) code 파라미터 내용이 인증 서버에 첨부된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;875&quot; data-origin-height=&quot;734&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uQn7x/btsLBzhryMQ/74xK29EQEk6R0VUt4a5ML0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uQn7x/btsLBzhryMQ/74xK29EQEk6R0VUt4a5ML0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uQn7x/btsLBzhryMQ/74xK29EQEk6R0VUt4a5ML0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuQn7x%2FbtsLBzhryMQ%2F74xK29EQEk6R0VUt4a5ML0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;875&quot; height=&quot;734&quot; data-origin-width=&quot;875&quot; data-origin-height=&quot;734&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*이 때, 접근 토큰을 직접 제공하지 않고 접근 토큰과 교환 가능한 인증 코드를 제공한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 인증 코드 제공 및 접근 토큰 교환&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1519&quot; data-origin-height=&quot;885&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oNoBA/btsLzbW4ZTk/kSpWaXKMfMRJSWCS1GUXP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oNoBA/btsLzbW4ZTk/kSpWaXKMfMRJSWCS1GUXP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oNoBA/btsLzbW4ZTk/kSpWaXKMfMRJSWCS1GUXP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoNoBA%2FbtsLzbW4ZTk%2FkSpWaXKMfMRJSWCS1GUXP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1519&quot; height=&quot;885&quot; data-origin-width=&quot;1519&quot; data-origin-height=&quot;885&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저의 인증 코드를 받은 이후 내 웹 서비스는 인증 코드, ClientID, Client Secret을 바탕으로 접근 토큰을 교환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 접근 토큰을 사용한 유저 정보 조회 및 서비스 제공&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1568&quot; data-origin-height=&quot;885&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCZSLn/btsLAvmKZHz/5MLIMaonXOuxQeCRpU5SMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCZSLn/btsLAvmKZHz/5MLIMaonXOuxQeCRpU5SMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCZSLn/btsLAvmKZHz/5MLIMaonXOuxQeCRpU5SMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCZSLn%2FbtsLAvmKZHz%2F5MLIMaonXOuxQeCRpU5SMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1568&quot; height=&quot;885&quot; data-origin-width=&quot;1568&quot; data-origin-height=&quot;885&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공적으로 접근 토큰을 교환하였다면, 내 웹 서비스는 허용된 범위 내에서 유저에 대한 정보를 조회할 수 있는 API를 resource server에 직접 요청할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;resource server가 제공하는 API를 통해 내 웹 서비스는 유저의 정보를 제공 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접근 토큰에는 유효 시간이 있기 때문에 필요하다면, 내 서비스는 접근 토큰을 갱신해야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;crsf token의 사용&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;csrf.png&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;820&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDoNdL/btsLByW8ZIR/ztRqFpEnpbMk5k9T9k84MK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDoNdL/btsLByW8ZIR/ztRqFpEnpbMk5k9T9k84MK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDoNdL/btsLByW8ZIR/ztRqFpEnpbMk5k9T9k84MK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDoNdL%2FbtsLByW8ZIR%2FztRqFpEnpbMk5k9T9k84MK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;942&quot; height=&quot;820&quot; data-filename=&quot;csrf.png&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;820&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;OAuth 2.0 인증 과정을 보면, 인증 서버에 state 파리미터로 스트링값이 포함된 것을 볼 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;인증 서버로의 요청 시에 csrf 토큰 값을 state 파라미터로 전달하며, 인증 서버는 crsf 토큰을 다시 참조할 수 있도록 유저 리다이렉션 주소에 파라미터로 첨부한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;Cross Site Request Forgery CSRF 크로스 사이트 요청 공격&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;공격자가 같은 겉모습을 가진 악의성 웹 사이트를 유저에게 사용하게 함으로써&lt;br /&gt;특정 요청에 대한 내용을 변경하여 공격자의 의도대로 웹 서버로 요청하게 하는 행위 이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 때, 웹 서비스는 자신이 제공한 웹 페이지가 현재 유저가 바라보는 웹 페이지와 일치하는 지 그리고 사용자의 요청이 자신의 웹 서비스 페이지로부터 온 것인지 확인할 필요가 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;웹 서비스는 CSRF 토큰을 발행하여 세션이나 보안 쿠키에 설정하여 사용자의 다음 요청에서 해당 토큰이 존재하고 일치하는 지 확인한다. 이에 따라 요청된 내용이 해당 토큰이 발행된 페이지에서 온 것 인지 검증한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1568&quot; data-origin-height=&quot;885&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VHBkf/btsLAA2rkNR/URVJVg83AXZGkDAmMllzt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VHBkf/btsLAA2rkNR/URVJVg83AXZGkDAmMllzt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VHBkf/btsLAA2rkNR/URVJVg83AXZGkDAmMllzt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVHBkf%2FbtsLAA2rkNR%2FURVJVg83AXZGkDAmMllzt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1568&quot; height=&quot;885&quot; data-origin-width=&quot;1568&quot; data-origin-height=&quot;885&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제) go 웹 서비스로 작성하여 google OAuth2.0을 사용하는 예제&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1735380012612&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package main

import (
	&quot;crypto/rand&quot;
	&quot;encoding/base64&quot;
	&quot;encoding/json&quot;
	&quot;log&quot;
	&quot;net/http&quot;

	&quot;github.com/gin-gonic/gin&quot;
	&quot;github.com/spf13/viper&quot;

	&quot;golang.org/x/oauth2&quot;
	&quot;golang.org/x/oauth2/google&quot;
)

type AuthProvider struct {
	Provider     string
	ClientId     string
	ClientSecret string
}

type AuthProviderConfig struct {
	Google AuthProvider
}

var (
	autoProviderConfig AuthProviderConfig
	oauth2Config       oauth2.Config
	oauth2State        string
)

func generateStateString() (string, error) {
	b := make([]byte, 32) // 32 bytes
	_, err := rand.Read(b)
	if err != nil {
		return &quot;&quot;, err
	}
	return base64.URLEncoding.EncodeToString(b), nil
}

func initConfig() error {
	viper.SetConfigName(&quot;config&quot;)
	viper.AddConfigPath(&quot;.&quot;)
	viper.SetConfigType(&quot;yaml&quot;)

	return viper.ReadInConfig()
}

func loadAuthProvider() {
	if viper.IsSet(&quot;auth-provider.google&quot;) {
		clientId := viper.GetString(&quot;auth-provider.google.client-id&quot;)
		clientSecret := viper.GetString(&quot;auth-provider.google.client-secret&quot;)
		redirectUri := viper.GetString(&quot;auth-provider.google.redirect-uri&quot;)
		scopes := viper.GetStringSlice(&quot;auth-provider.google.scopes&quot;)

		autoProviderConfig.Google = AuthProvider{
			Provider:     &quot;google&quot;,
			ClientId:     clientId,
			ClientSecret: clientSecret,
		}
		log.Printf(&quot;Google auth provider loaded with client id: %s&quot;, clientId)

		oauth2Config = oauth2.Config{
			ClientID:     clientId,
			ClientSecret: clientSecret,
			RedirectURL:  redirectUri,
			Scopes:       scopes,
			Endpoint:     google.Endpoint,
		}
	}
}

func getPing(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{&quot;message&quot;: &quot;server is healthy&quot;})
}

func userAuthCallbackGoogle(c *gin.Context) {
	code := c.DefaultQuery(&quot;code&quot;, &quot;&quot;)
	state := c.DefaultQuery(&quot;state&quot;, &quot;&quot;)

	if code == &quot;&quot; || state == &quot;&quot; {
		c.JSON(http.StatusBadRequest, gin.H{&quot;error&quot;: &quot;missing code or state&quot;})
		return
	}

	// csrf attack check
	originalState, err := c.Cookie(&quot;oauth2_state&quot;)
	if err != nil || originalState == &quot;&quot; {
		c.JSON(http.StatusBadRequest, gin.H{&quot;error&quot;: &quot;missing state cookie&quot;})
		return
	}

	// exchange the code for a token
	token, err := oauth2Config.Exchange(c, code)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{&quot;error&quot;: &quot;failed to exchange token&quot;})
		return
	}

	// use the token to fetch user information
	client := oauth2Config.Client(c, token)
	resp, err := client.Get(&quot;https://www.googleapis.com/oauth2/v2/userinfo&quot;)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{&quot;error&quot;: &quot;failed to fetch user info&quot;})
		return
	}
	defer resp.Body.Close()

	// parse user data
	var user map[string]interface{}
	if err := json.NewDecoder(resp.Body).Decode(&amp;amp;user); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{&quot;error&quot;: &quot;failed to parse user info&quot;})
		return
	}

	c.JSON(http.StatusOK, gin.H{&quot;email&quot;: user[&quot;email&quot;], &quot;name&quot;: user[&quot;name&quot;]})
}

func userAuthGoogle(c *gin.Context) {
	// generate a random state string
	oauth2State, err := generateStateString()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{&quot;error&quot;: &quot;failed to generate state&quot;})
		return
	}

	// set cookie with state
	// use secure when in production as https is required
	c.SetCookie(&quot;oauth2_state&quot;, oauth2State, 3600, &quot;&quot;, &quot;&quot;, false, true)

	authURL := oauth2Config.AuthCodeURL(oauth2State, oauth2.AccessTypeOnline)

	log.Printf(&quot;Redirecting to %s&quot;, authURL)

	c.Redirect(http.StatusFound, authURL)
}

func main() {

	readConfigErr := initConfig()
	if readConfigErr != nil {
		log.Fatalf(&quot;Error reading config file, %s&quot;, readConfigErr)
	}

	loadAuthProvider()

	port := viper.GetString(&quot;server.port&quot;)
	mode := viper.GetString(&quot;server.mode&quot;)

	gin.SetMode(mode)

	router := gin.Default()
	router.GET(&quot;/ping&quot;, getPing)
	router.GET(&quot;/auth/google&quot;, userAuthGoogle)
	router.GET(&quot;/auth/google/callback&quot;, userAuthCallbackGoogle)

	router.Run(&quot;localhost:&quot; + port)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;main.go&lt;/p&gt;
&lt;pre id=&quot;code_1735380061415&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server:
  port: &quot;8080&quot;

logging:
  level: &quot;info&quot;
  format: &quot;json&quot;

auth-provider:
  google:
    client-id: your-client-id
    client-secret: your-client-secret
    redirect-uri: &quot;http://localhost:8080/auth/google/callback&quot;
    scopes:
      - &quot;openid&quot;
      - &quot;email&quot;
      - &quot;profile&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;config.yml&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>개발 이론/Web</category>
      <author>로파이</author>
      <guid isPermaLink="true">https://narakit.tistory.com/318</guid>
      <comments>https://narakit.tistory.com/318#entry318comment</comments>
      <pubDate>Sat, 28 Dec 2024 19:02:05 +0900</pubDate>
    </item>
    <item>
      <title>Docker In Action 12. First-class configuration abstractions</title>
      <link>https://narakit.tistory.com/316</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;도커 config와 secret에 대해 알아본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Configuration 구성 설정 파일은 어플리케이션을 실행할 때 필요한 설정 파일을 의미하며, DB 커넥션 스트링, 각종 제약과 비즈니스 로직에 사용되는 설정값을 파일을 통해 설정하는 것을 의미.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;12.1 구성 설정 배포와 관리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 서비스는 어플리케이션을 정의하는 이미지에 의존하는 것처럼 configuration 리소스에 의존한다. 구성 설정이나 시크릿 파일이 누락되었다면, 어플리케이션은 실행 되지 않거나 잘 못 된 행동을 유발한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;12.2 어플리케이션과 구성 설정 분리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;greetings 서비스는 &quot;Hello World!&quot;를 서로 다른 언어로 출력하는 서비스이다. 이 서비스의 개발자는 유저의 요청이 오면 한 인사말을 응답으로 주고 싶어한다. 번역가를 통해 검증 받은 인사말들을 다음과 같은 config.common.yml 파일에 설정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;config.common.yml&lt;/p&gt;
&lt;pre id=&quot;code_1734177645591&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;greetings:
	- 'Hello World!'
	- 'Hola Mundo!'
	- 'Hallo Welt!'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스는 실제 배포 환경을 인식하여 기본 인사말에 더해 특정 인사말들을 추가하길 원한다. 개발팀은 dev, stage, 그리고 prod 환경을 관리하고 dev 환경에서는 다음과 같은 구성을 사용하고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;config.dev.yml&lt;/p&gt;
&lt;pre id=&quot;code_1734177851293&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;greetings:
	- 'Orbis Terrarum slave!'
	- 'Bonjour le monde!'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 배포할 때 필요한 것은 배포 환경에 따른 구성 설정 파일을 컨테이너 내부로 주입하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;greetings 서비스는 다음 git 소스에 정의된 Docker Compose 파일을 사용하여 도커 스웜에 스택으로 배포할 것이다.&lt;br /&gt;&lt;a href=&quot;https://github.com/dockerinaction/ch12_greetings&quot;&gt;GitHub - dockerinaction/ch12_greetings: This repository contains the greetings service example used in Chapter 12 of Docker in Action, 2ed.&lt;/a&gt; &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포에 사용할 docker.compose.yml&lt;/p&gt;
&lt;pre id=&quot;code_1734178084450&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Docker Compose file reference: https://docs.docker.com/compose/compose-file/
version: '3.7'

configs:
  env_specific_config:
    file: ./api/config/config.${DEPLOY_ENV:-prod}.yml

services:

  api:
      image: ${IMAGE_REPOSITORY:-dockerinaction/ch12_greetings}:api
      ports:
        - '8080:8080'
        - '8443:8443'
      user: '1000'
      configs:
        #- env_specific_config # short form mounts the config file in as a file named `env_specific_config`
        - source: env_specific_config
          target: /config/config.${DEPLOY_ENV:-prod}.yml
          uid: '1000'
          gid: '1000'
          mode: 0400 #default is 0444 - readonly for all users
      secrets: []
      environment:
        DEPLOY_ENV: ${DEPLOY_ENV:-prod}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경변수 DEPLOY_ENV에 따라 적절한 config 파일 i.e. config.dev.yml을 사용하여 어플리케이션이 해당 파일을 주입 받아 실행할 수 있도록 배포 설정을 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;config resource 다루기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker config resource는 Swarm 클러스터 객체로 어플리케이션의 런타임 필요로 저장할 때 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1734178397594&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;configs:
  env_specific_config:
    file: ./api/config/config.${DEPLOY_ENV:-prod}.yml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 윗 부분에 정의된 configs 엔트리에 env_specific_config는 환경 변수 DEPLOY_ENV에 결정된 파일 이름을 가리킨다.&lt;br /&gt;도커 스택이 생성될 때 해당 파일은 config resource 로써 도커 클러스터 내부에 저장된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734178469614&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;services:

  api:
      image: ${IMAGE_REPOSITORY:-dockerinaction/ch12_greetings}:api
      ports:
        - '8080:8080'
        - '8443:8443'
      user: '1000'
      configs:
        #- env_specific_config # short form mounts the config file in as a file named `env_specific_config`
        - source: env_specific_config
          target: /config/config.${DEPLOY_ENV:-prod}.yml
          uid: '1000'
          gid: '1000'
          mode: 0400 #default is 0444 - readonly for all users&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 정의에 configs 엔트리에서 볼륨을 지정하듯 config resource를 주입한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 파일의 uid, gid 그리고 접근 권한이 설정된다. (readonly to user)&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;어플리케이션 배포&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경변수를 설정하고 어플리케이션 greetings_dev를 배포한다.&lt;/p&gt;
&lt;pre id=&quot;code_1734178742942&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$env:DEPLOY_ENV=&quot;dev&quot; # on windows
DEPLOY_ENV=dev # on linux
docker stack deploy --compose-file docker-compose.yml greetings_dev&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;localhost:8080 포트의 웹 주소로 들어가면 다음 응답을 받을 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;55&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXacWs/btsLjKDHnmY/pKgEaSIbZZNkoLkwFQE35k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXacWs/btsLjKDHnmY/pKgEaSIbZZNkoLkwFQE35k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXacWs/btsLjKDHnmY/pKgEaSIbZZNkoLkwFQE35k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXacWs%2FbtsLjKDHnmY%2FpKgEaSIbZZNkoLkwFQE35k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;634&quot; height=&quot;55&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;55&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;localhost:8080/greeting 요청에 인사말이 응답으로 오는 지 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734179582458&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Hello World!
Hallo Welt!
Orbis Terrarum salve!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;도커 config 관리하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker config는 subcommand로 create, inspect, ls, rm을 지원한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker config inspect greetings_dev_env_specific_cofnig로 예제로 만들어진 config resource를 자세히 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당이름은 도커 스택의 이름 greetings_dev를 prefix로 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1053&quot; data-origin-height=&quot;310&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZFOty/btsLiWrvGUD/MZfDsrdY1JismQC7H0xUOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZFOty/btsLiWrvGUD/MZfDsrdY1JismQC7H0xUOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZFOty/btsLiWrvGUD/MZfDsrdY1JismQC7H0xUOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZFOty%2FbtsLiWrvGUD%2FMZfDsrdY1JismQC7H0xUOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1053&quot; height=&quot;310&quot; data-origin-width=&quot;1053&quot; data-origin-height=&quot;310&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Data는 실제 config 데이터를 base64 인코딩한 값이며 비밀성 유지를 지키지지는 않는다. 단지 도커 스웜 클러스터사이의 저장과 이동을 위해 인코딩되었다. 스웜은 클러스터 중앙 저장소에서 꺼내어 서비스 태스크의 파일 시스템에 배치한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 config 는 create와 rm 만 지원하며, 업데이트는 허용하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 config 내용만 업데이트하여 같은 배포를 실행하면 실패하게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1734180094792&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker stack deploy --compose-file docker-compose.yml greetings_dev&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;18&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YxnGe/btsLjj7F3vk/dSw9BYYlK4iPjteW246ZHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YxnGe/btsLjj7F3vk/dSw9BYYlK4iPjteW246ZHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YxnGe/btsLjj7F3vk/dSw9BYYlK4iPjteW246ZHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYxnGe%2FbtsLjj7F3vk%2FdSw9BYYlK4iPjteW246ZHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1146&quot; height=&quot;18&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;18&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;도커 서비스와 의존성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스는 이제 이미지와 config resource에 의존한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;config resource가 존재하지 않거나 변경되었다면 서비스는 시작하지 않는다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Depdendency&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;application_image&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;config_resource&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;service greetings_dev&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;dockerinaction/ch12_greetings:api&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;greetings_dev_env_specific_config&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 inspection&lt;/p&gt;
&lt;pre id=&quot;code_1734180422186&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker service inspect --format '{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}' greetings_dev_api&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행하고 있는 greetings_dev_api 서비스가 참조하는 ConfigID는 생성된 config resource의 unique ID를 가리킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 정의에 config가 바인딩되어 정의되어 있는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1734180435359&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[{&quot;File&quot;:{&quot;Name&quot;:&quot;/config/config.dev.yml&quot;,&quot;UID&quot;:&quot;1000&quot;,&quot;GID&quot;:&quot;1000&quot;,&quot;Mode&quot;:256},&quot;ConfigID&quot;:&quot;qss45j65h01kjpwxw1npjvz08&quot;,&quot;ConfigName&quot;:&quot;greetings_dev_env_specific_config&quot;}]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;config는 이미 사용중이라면 삭제가 불가능하고 이미 있는 이름으로 생성도 불가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;config를 변경하고자 한다면, config 도 새로운 리소스 이름으로 실행해야한다. 마치 이미지에 새로운 배포 태그를 붙여 새로운 배포를 실행하는 것과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;t0 : first application deployment with greetings_dev_env_specific_config&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;t1 : changes configuration as greetings_dev_env_specific_config_v1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;t2 : update features and change configuration as greetings_dev_env_specific_config_v2&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;12.3 Secrets, 특별한 구성 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Secret은 비밀번호나 API key, private encryption 등 절대 외부로 노출되면 안되는 설정 값들을 의미한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1246&quot; data-origin-height=&quot;629&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DRpht/btsLjbPrEvi/BMKdxDyLWGAfC8iGC1KyH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DRpht/btsLjbPrEvi/BMKdxDyLWGAfC8iGC1KyH0/img.png&quot; data-alt=&quot;도커 bootstrap 과정 중 서비스 인증 과정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DRpht/btsLjbPrEvi/BMKdxDyLWGAfC8iGC1KyH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDRpht%2FbtsLjbPrEvi%2FBMKdxDyLWGAfC8iGC1KyH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;646&quot; height=&quot;326&quot; data-origin-width=&quot;1246&quot; data-origin-height=&quot;629&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;도커 bootstrap 과정 중 서비스 인증 과정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어플리케이션이 Secret과 함께 실행하지 않는다면 외부 인증 주체가 어플리케이션을 인증하고 secret을 제공하는 수단을 사용해야한다. 이러한 수단으로써 도커는 부트스트랩 과정에서 신뢰있는 시크릿 제공을 가능하게 한다. 클러스터링 서비스 과정에서 암호화된 통신을 사용하여 자신의 서비스 Service ID로 클러스터링 매니저에 인증을 하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 스웜은 Service ID를 기반으로 어떤 Secret을 Service 태스크가 접근할 수 있는 지 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커는 tmpfs 파일 시스템을 사용하여 읽기만 가능한 인메모리 파일 시스템을 컨테이너에 마운트한다. 기본값으로 secrets은 /run/secrets 경로에 마운트 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어플리케이션은 어떤 파일을 읽을 지 알고 있고 도커로 부터 제공받은 secret 파일을 읽어 비밀번호나 API_KEY와 같은 것을 사용할 수 있게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;greetings 서비스 예제)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTPS 서비스를 위하여 CA의 private key를 docker secret으로 public key을 docker config로 주입한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734182207211&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$env:DEPLOY_ENV=&quot;prod&quot; # on windows
DEPLOY_ENV=prod # on linux

docker stack deploy --compose-file docker-compose.yml --compose-file docker-compose.prod.yml greetings_prod&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위를 실행하면 다음과 같은 오류가 발생한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;554&quot; data-origin-height=&quot;53&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvnMCG/btsLjbaUoMS/EgBXxQf3hCnHF4SkPGsWNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvnMCG/btsLjbaUoMS/EgBXxQf3hCnHF4SkPGsWNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvnMCG/btsLjbaUoMS/EgBXxQf3hCnHF4SkPGsWNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvnMCG%2FbtsLjbaUoMS%2FEgBXxQf3hCnHF4SkPGsWNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;554&quot; height=&quot;53&quot; data-origin-width=&quot;554&quot; data-origin-height=&quot;53&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734182408102&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;configs:
  ch12_greetings_svc-prod-TLS_CERT_V1:
    external: true

secrets:
  ch12_greetings-svc-prod-TLS_PRIVATE_KEY_V1:
    external: true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker-compose.prod.yml 파일에는 config ch12_greetings_svc-prod-TLS_CERT_V1와 ch12_greetings-svc-prod-TLS_PRIVATE_KEY_V1가 자동 생성이 아닌 &quot;external&quot; 전역에 정의된 값을 사용하도록 되어 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734182519336&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker secret create ch12_greetings-svc-prod-TLS_PRIVATE_KEY_V1 &quot;./api/config/insecure.key&quot;
docker config create ch12_greetings_svc-prod-TLS_CERT_V1 &quot;./api/config/insecure.crt&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker secret create와 docker config create를 사용하여 적절한 secret과 config를 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 서비스를 시작하면, https 로 시작한 greetings_prod_api를 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1044&quot; data-origin-height=&quot;54&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bq1d23/btsLjtoKkP4/8WMzKnkFqY7n5ElAysdKlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bq1d23/btsLjtoKkP4/8WMzKnkFqY7n5ElAysdKlK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bq1d23/btsLjtoKkP4/8WMzKnkFqY7n5ElAysdKlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbq1d23%2FbtsLjtoKkP4%2F8WMzKnkFqY7n5ElAysdKlK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1044&quot; height=&quot;54&quot; data-origin-width=&quot;1044&quot; data-origin-height=&quot;54&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 로그를 확인하여 참조한 https CA 인증서의 private key와 public key 위치를 확인한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1082&quot; data-origin-height=&quot;190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/S5Ax2/btsLiSo9G0T/kuCN6nk0gYTS11Nykfeoo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/S5Ax2/btsLiSo9G0T/kuCN6nk0gYTS11Nykfeoo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/S5Ax2/btsLiSo9G0T/kuCN6nk0gYTS11Nykfeoo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FS5Ax2%2FbtsLiSo9G0T%2FkuCN6nk0gYTS11Nykfeoo0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1082&quot; height=&quot;190&quot; data-origin-width=&quot;1082&quot; data-origin-height=&quot;190&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;https://localhost:8443 웹 주소에서 정상 출력됨을 확인한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;669&quot; data-origin-height=&quot;61&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qrSJm/btsLjM2Bzcs/TFqpE3FXUyIXmoT6k0b0t0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qrSJm/btsLjM2Bzcs/TFqpE3FXUyIXmoT6k0b0t0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qrSJm/btsLjM2Bzcs/TFqpE3FXUyIXmoT6k0b0t0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqrSJm%2FbtsLjM2Bzcs%2FTFqpE3FXUyIXmoT6k0b0t0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;669&quot; height=&quot;61&quot; data-origin-width=&quot;669&quot; data-origin-height=&quot;61&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요약&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 어플리케이션은 배포되는 환경에 맞게 동작을 변경해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 도커 config와 secret 자원은 배포되는 환경에 맞게 동작되도록 어플리케이션을 조작하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- secrets는 비밀성을 요구하는 특별 설정값들이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 도커 스웜은 trust 체인을 사용하여 도커 서비스가 시크릿이 특정 어플리케이션에 제공될 수 있음을 인증한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 도커는 config와 secret 제공을 위해 tmpfs 파일 시스템을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 배포 프로세스는 자동화를 위해 config와 secret resource에 이름을 붙이는 명명법으로 관리하는 것을 권고한다.&lt;/p&gt;</description>
      <category>Cloud/Docker</category>
      <author>로파이</author>
      <guid isPermaLink="true">https://narakit.tistory.com/316</guid>
      <comments>https://narakit.tistory.com/316#entry316comment</comments>
      <pubDate>Sat, 14 Dec 2024 22:39:22 +0900</pubDate>
    </item>
    <item>
      <title>Docker In Action 11. Services with Docker and Compose</title>
      <link>https://narakit.tistory.com/315</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;도커 서비스 구축&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서비스란&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떠한 프로세스 process, 기능 functionality 혹은 데이터가 네트워크를 통해 이용가능할 때 서비스라고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 스웜 노드를 통한 서비스 구축 및 Docker Compose 파일 작성을 통해 선언적 방식으로 서비스를 실행하는 방법을 알아본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;11.1 Service &quot;Hello World&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hello-world 서비스 : 간단한 hello-world 컨테이너를 하나 실행하는 명령어 예제.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734161569604&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker swarm init // establish service abstraction
docker service create
	--publish 8080:80
    --name hello-world
    dockerinaction/ch11_service_hw:v1 // starts local server listens on 8080&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 서비스는 docker swarm mode가 실행될 때 가능하다. docker service create 명령어는 컨테이너 실행과 비슷하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1478&quot; data-origin-height=&quot;169&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PSJLQ/btsLiSCx4rd/ACbc2HO6dHd2XklMUzfTIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PSJLQ/btsLiSCx4rd/ACbc2HO6dHd2XklMUzfTIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PSJLQ/btsLiSCx4rd/ACbc2HO6dHd2XklMUzfTIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPSJLQ%2FbtsLiSCx4rd%2FACbc2HO6dHd2XklMUzfTIk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;562&quot; height=&quot;64&quot; data-origin-width=&quot;1478&quot; data-origin-height=&quot;169&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker swarm init : 도커 엔진은 도커 서비스 오케스트레이션 (서비스를 구성하는 노드를 유지하는 도구) 기능을 제공하는 docker swarm node를 실행한다. 내부적으로 오케스트레이션을 위한 데이터베이스를 시작하는 것을 포함한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1651&quot; data-origin-height=&quot;367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6C4Wj/btsLjsiV9wp/AuKReBB5iexgP1k1MFSrYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6C4Wj/btsLjsiV9wp/AuKReBB5iexgP1k1MFSrYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6C4Wj/btsLjsiV9wp/AuKReBB5iexgP1k1MFSrYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6C4Wj%2FbtsLjsiV9wp%2FAuKReBB5iexgP1k1MFSrYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;603&quot; height=&quot;134&quot; data-origin-width=&quot;1651&quot; data-origin-height=&quot;367&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker service create는 8080 포트에서 서비스하는 컨테이너를 하나 실행한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 스웜은 &quot;hello-world 컨테이너 실행&quot;을 하는 &quot;task&quot; 작업을 정의서를 가지고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1651&quot; data-origin-height=&quot;367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chsgEv/btsLh3yeD3C/ANwpAFjmelbnVchjIHKCI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chsgEv/btsLh3yeD3C/ANwpAFjmelbnVchjIHKCI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chsgEv/btsLh3yeD3C/ANwpAFjmelbnVchjIHKCI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchsgEv%2FbtsLh3yeD3C%2FANwpAFjmelbnVchjIHKCI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;630&quot; height=&quot;140&quot; data-origin-width=&quot;1651&quot; data-origin-height=&quot;367&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 작업을 통해 실제 컨테이너 하나가 실행되도록 Swam Node는 서비스를 유지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker service ps 를 통해 실행중인 서비스 상태를 확인한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1182&quot; data-origin-height=&quot;106&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAgzpv/btsLiCz6hXd/TJftUFSukvH6Dueh3vTLO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAgzpv/btsLiCz6hXd/TJftUFSukvH6Dueh3vTLO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAgzpv/btsLiCz6hXd/TJftUFSukvH6Dueh3vTLO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAgzpv%2FbtsLiCz6hXd%2FTJftUFSukvH6Dueh3vTLO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1182&quot; height=&quot;106&quot; data-origin-width=&quot;1182&quot; data-origin-height=&quot;106&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;서비스의 복구와 복제 기능 &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스는 실행되는 노드가 목표로하는 노드 수를 유지하려고 한다. 현재 유지해야하는 노드 (컨테이너)가 한 개이고 이 노드가 중단된다면, 서비스는 노드를 하나 다시 실행한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;791&quot; data-origin-height=&quot;51&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BvdB5/btsLiA97RcX/CdwKsNzRHa2ws1DU4iSImk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BvdB5/btsLiA97RcX/CdwKsNzRHa2ws1DU4iSImk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BvdB5/btsLiA97RcX/CdwKsNzRHa2ws1DU4iSImk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBvdB5%2FbtsLiA97RcX%2FCdwKsNzRHa2ws1DU4iSImk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;791&quot; height=&quot;51&quot; data-origin-width=&quot;791&quot; data-origin-height=&quot;51&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REPLICAS 1/1 로 표기되어 있는 의미는 한 개의 노드를 반드시 유지하도록 되어 있다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 컨테이너 하나를 강제로 중지한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1183&quot; data-origin-height=&quot;71&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6OUEj/btsLiGvDzP3/fk1kFsajfKgcEyKip0jI0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6OUEj/btsLiGvDzP3/fk1kFsajfKgcEyKip0jI0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6OUEj/btsLiGvDzP3/fk1kFsajfKgcEyKip0jI0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6OUEj%2FbtsLiGvDzP3%2Ffk1kFsajfKgcEyKip0jI0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1183&quot; height=&quot;71&quot; data-origin-width=&quot;1183&quot; data-origin-height=&quot;71&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 잠시후 docker service ps hello-world를 통해 서비스 상태를 확인한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;73&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0Rp6z/btsLjbaPA0P/5BGRqzKChEiOLaUKPjYkM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0Rp6z/btsLjbaPA0P/5BGRqzKChEiOLaUKPjYkM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0Rp6z/btsLjbaPA0P/5BGRqzKChEiOLaUKPjYkM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0Rp6z%2FbtsLjbaPA0P%2F5BGRqzKChEiOLaUKPjYkM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1221&quot; height=&quot;73&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;73&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중지한 컨테이너 기록이 있고 그 이후에 다시 컨테이너를 새로 실행한 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DESIRED STATE, CURRENT STATE&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Desired State는 유저가 서비스를 통해 유지되어야할 상태를 나타내고 Current State는 현재 상태를 의미한다. 오케스트레이션 도구는 서비스 상태를 추적하여 상태 변화를 감지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker service inspect 서비스 인스펙션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 이름, Service ID, 버전, 타임스탬프 등등 서비스 상태를 표시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;노드 스케일 설정 변경&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker service scale hello-world=3&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 스웜으로 실행중인 서비스 노드 수를 조절 가능하며 효력은 즉시 일어난다. 필요한 컨테이너 수 만큼 실행이 되며 각 컨테이너 이름은 hello-world.1, hello-world.2, hello-world.3 을 prefix로 하여 실행된다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1213&quot; data-origin-height=&quot;90&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2mCAp/btsLiiPClgZ/CYGkIek8trhvEXlzipyqT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2mCAp/btsLiiPClgZ/CYGkIek8trhvEXlzipyqT1/img.png&quot; data-alt=&quot;docker service ps&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2mCAp/btsLiiPClgZ/CYGkIek8trhvEXlzipyqT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2mCAp%2FbtsLiiPClgZ%2FCYGkIek8trhvEXlzipyqT1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1213&quot; height=&quot;90&quot; data-origin-width=&quot;1213&quot; data-origin-height=&quot;90&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;docker service ps&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1192&quot; data-origin-height=&quot;71&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/loaya/btsLiJ6OgfA/OfA3rKZCqiBjy32st1fDJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/loaya/btsLiJ6OgfA/OfA3rKZCqiBjy32st1fDJk/img.png&quot; data-alt=&quot;docker container ls&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/loaya/btsLiJ6OgfA/OfA3rKZCqiBjy32st1fDJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Floaya%2FbtsLiJ6OgfA%2FOfA3rKZCqiBjy32st1fDJk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1192&quot; height=&quot;71&quot; data-origin-width=&quot;1192&quot; data-origin-height=&quot;71&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;docker container ls&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;자동화된 롤아웃&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 실행 파일 교체 등 서비스 업데이트를 위한 명령을 알아본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734163435602&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker service update \
	--image dockerinaction/ch11_service_hw:v2 \
	--update-order stop-first \
	--update-parallelism 1 \
	--update-delay 30s
	hello-world&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker service update는 새로운 이미지 ch11_service_hw:v2 로 실행되는 컨테이너로 교체하는 작업을 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 작업은 하나씩 기존 서비스를 중지하고 새로운 이미지로 실행하는 서비스를 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 서비스 업데이트 사이에 30초의 딜레이가 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 업데이트가 모두 완료되면, Service converaged 사인이 출력되고 정상 처리되었음을 알 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1470&quot; data-origin-height=&quot;140&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NCkIc/btsLisYJJJq/Kuh4GeETrka7Q26X0Xis7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NCkIc/btsLisYJJJq/Kuh4GeETrka7Q26X0Xis7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NCkIc/btsLisYJJJq/Kuh4GeETrka7Q26X0Xis7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNCkIc%2FbtsLisYJJJq%2FKuh4GeETrka7Q26X0Xis7K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1470&quot; height=&quot;140&quot; data-origin-width=&quot;1470&quot; data-origin-height=&quot;140&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서비스 헬스 체크와 롤백&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비정상적인 서비스를 업데이트 했을 때, 서비스 업데이트 사항을 롤백할 수 있는 기능을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 영원히 실행하지 않는 서비스를 하나 실행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1734164614974&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker service update \
	--image dockerinaction/ch11_service_hw:start-failure \
	hello-world&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태가 &quot;실패&quot; 상태로 유지되고 있음을 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 롤백 처리를 실행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1734164723384&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker service update --rollback hello-world&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 배포 시 실패가 감지 되면 자동 롤백처리를 실행하는 옵션을 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1734164798200&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker service update --update-failure-action rollback --update-max-failure-ratio 0.7 --image dockerinaction/ch11_service_hw:start-failure hello-world&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헬스 체크&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;v2 이미지는 헬스 체크 기능을 내장하고 있다. 내장 기능 httpping 프로그램을 통해 컨테이너가 localhost, '/' 경로에서 정상 응답 200을 받고 있다는 것을 확인하고 컨테이너 상태가 &quot;health&quot; 임을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;92&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtdqMM/btsLikzKnFc/QyqRe9fnob6xtSaCcPRynk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtdqMM/btsLikzKnFc/QyqRe9fnob6xtSaCcPRynk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtdqMM/btsLikzKnFc/QyqRe9fnob6xtSaCcPRynk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdtdqMM%2FbtsLikzKnFc%2FQyqRe9fnob6xtSaCcPRynk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1170&quot; height=&quot;92&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;92&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 이미지의 도커 파일에는 다음과 같이 헬스 체크를 하도록 명시되어 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1734165064652&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;HEALTHCHECK --interval=10s CMD [&quot;/bin/httpping&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헬스 체크 기능이 없는 이미지를 사용하면 더 이상 컨테이너 상태는 &quot;healthy&quot; 인 지 알 수 없게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1734165124506&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker service update \
	--update-failure-action rollback \
	--image dockerinaction/ch11_service_hw:no-health \
	hello-world&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;87&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s6RZX/btsLjd7xnbB/mn6k9kjqemGTkBIgR74uG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s6RZX/btsLjd7xnbB/mn6k9kjqemGTkBIgR74uG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s6RZX/btsLjd7xnbB/mn6k9kjqemGTkBIgR74uG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs6RZX%2FbtsLjd7xnbB%2Fmn6k9kjqemGTkBIgR74uG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1197&quot; height=&quot;87&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;87&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 중단&lt;/p&gt;
&lt;pre id=&quot;code_1734165688321&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker service rm hello-world&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제로 사용한 서비스를 중단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Compose V3를 사용한 도커 서비스 환경 구축&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령적 방식이 아닌 파일을 사용하여 구축할 서비스를 정의하고 이를 통해 서비스를 실행하는 방법을 알아본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker stack&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 스택은 모든 도커 서비스, 볼륨, 네트워크 그리고 다른 설정 요소들의 구성을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compose V3 파일은 도커 스택을 정의하며, YAML 포맷으로 작성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;database.yml 예제&lt;/p&gt;
&lt;pre id=&quot;code_1734165472567&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;version: &quot;3.7&quot;
volumes:
  pgdata:
services:
  postgres:
    image: dockerinaction/postgres:11-alpine
    volumes:
      - type: volume
        source: pgdata
        target: /var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: example
  
  mariadb:
    image: dockerinaction/mariadb:10-bionic
    environment:
      MYSQL_ROOT_PASSWORD: example
    
  adminer:
    image: dockerinaction/adminer:4
    ports:
      - 8080:8080
    deploy:
      replicas: 2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;volumes : 도커의 볼륨을 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;services : 도커의 서비스를 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 엔트리의 구성 요소는 postgres, mariadb, adminer 서비스를 구성하는 것을 알 수 있따.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 서비스는 사용할 이미지, 볼륨, 환경 변수, 포트, 복제 노드 수 등을 작성하여 실행할 서비스의 설정을 구성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;도커 스택 배포하기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734165605514&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker stack deploy -c database.yml my-databases&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;894&quot; data-origin-height=&quot;106&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kNxoZ/btsLjna0iWB/LSKtkUrqBPBvHDif5j6kW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kNxoZ/btsLjna0iWB/LSKtkUrqBPBvHDif5j6kW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kNxoZ/btsLjna0iWB/LSKtkUrqBPBvHDif5j6kW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkNxoZ%2FbtsLjna0iWB%2FLSKtkUrqBPBvHDif5j6kW0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;894&quot; height=&quot;106&quot; data-origin-width=&quot;894&quot; data-origin-height=&quot;106&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 상태 확인하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker service ls 서비스 상태 확인하기&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;857&quot; data-origin-height=&quot;87&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sRley/btsLjEwOdlY/HiEBVBLfIL2VL5S6TdBNl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sRley/btsLjEwOdlY/HiEBVBLfIL2VL5S6TdBNl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sRley/btsLjEwOdlY/HiEBVBLfIL2VL5S6TdBNl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsRley%2FbtsLjEwOdlY%2FHiEBVBLfIL2VL5S6TdBNl1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;857&quot; height=&quot;87&quot; data-origin-width=&quot;857&quot; data-origin-height=&quot;87&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker stack ps로 스택 상태를 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;989&quot; data-origin-height=&quot;88&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cV29UD/btsLihXs04T/LiqJSpLsjwVHRVfcEkHDG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cV29UD/btsLihXs04T/LiqJSpLsjwVHRVfcEkHDG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cV29UD/btsLihXs04T/LiqJSpLsjwVHRVfcEkHDG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcV29UD%2FbtsLihXs04T%2FLiqJSpLsjwVHRVfcEkHDG0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;989&quot; height=&quot;88&quot; data-origin-width=&quot;989&quot; data-origin-height=&quot;88&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yml 파일을 수정하여 adminer 서비스의 replicas 수를 조정하고 다시 배포해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조정된 수로 실행된 adminer 서비스 수를 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;로드밸런싱, 서비스 디스커버리, 그리고 네트워크&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;adminer 웹 서비스는 8080 포트를 통해 접근 가능하다는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qJRIy/btsLjogHxWm/0Ln6RQrynwh6H7pEKShTq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qJRIy/btsLjogHxWm/0Ln6RQrynwh6H7pEKShTq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qJRIy/btsLjogHxWm/0Ln6RQrynwh6H7pEKShTq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqJRIy%2FbtsLjogHxWm%2F0Ln6RQrynwh6H7pEKShTq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;648&quot; height=&quot;367&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;367&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 컨테이너는 두 개가 실행되고 있으며 컨테이너에 할당된 포트 번호는 8080과는 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커는 virtual IP를 생성하여 서비스를 중재하고 들어오는 요청을 내부 실행되는 컨테이너에게 적절히 전달하는 로드 밸런싱을 수행한다. 호스트의 인터페이스로 부터 요이 들어오면 서비스의 VIP로 포워딩을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1899&quot; data-origin-height=&quot;601&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcz8px/btsLiJMwQLN/5E4u3XY7KPPOyAc2LCt6XK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcz8px/btsLiJMwQLN/5E4u3XY7KPPOyAc2LCt6XK/img.png&quot; data-alt=&quot;my-databases 서비스의 네트워크 구성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcz8px/btsLiJMwQLN/5E4u3XY7KPPOyAc2LCt6XK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbcz8px%2FbtsLiJMwQLN%2F5E4u3XY7KPPOyAc2LCt6XK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;762&quot; height=&quot;241&quot; data-origin-width=&quot;1899&quot; data-origin-height=&quot;601&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;my-databases 서비스의 네트워크 구성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;ingress 네트워크&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker service inspect my-databases_adminer 를 실행하면, 다음과 같이 일부분에서 호스트 인터페이스와 연결된 ingress 네트워크와 두 개의 adminer 서비스 VIP들이 있는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;538&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uNZW6/btsLjaCZYQy/qb9cxdRuck3MkTa84eiKjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uNZW6/btsLjaCZYQy/qb9cxdRuck3MkTa84eiKjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uNZW6/btsLjaCZYQy/qb9cxdRuck3MkTa84eiKjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuNZW6%2FbtsLjaCZYQy%2Fqb9cxdRuck3MkTa84eiKjk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;634&quot; height=&quot;538&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;538&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker network inspect ingress&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;684&quot; data-origin-height=&quot;397&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvpgBq/btsLjIFYQP3/wc4SNpQdk6Ck9lcjQmm1UK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvpgBq/btsLjIFYQP3/wc4SNpQdk6Ck9lcjQmm1UK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvpgBq/btsLjIFYQP3/wc4SNpQdk6Ck9lcjQmm1UK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvpgBq%2FbtsLjIFYQP3%2Fwc4SNpQdk6Ck9lcjQmm1UK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;684&quot; height=&quot;397&quot; data-origin-width=&quot;684&quot; data-origin-height=&quot;397&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ingress 네트워크를 자세히 보면, 두 adminer 컨테이너에 대한 Endpoint와 ingress 네트워크의 Endpoint를 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 도커 내부에서 사용되는 주소이며 로드 밸런서가 외부 요청을 전달할 주소이다. 외부 호스트 인터페이스와 연결된 ingress-sbox는 내부 IP로 10.0.0.2/24 주소를 갖는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;스택 기본 네트워크 : my-databases_default&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택에 참여하는 모든 서비스는 기본 네트워크를 통해 통신이 가능하다. 각 컨테이너의 자신의 Endpoint를 가진 고유 네트워크는 스택 기본 네트워크와 연결되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker network inspect my-databases_default를 통해 상세 내용을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;717&quot; data-origin-height=&quot;639&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpGNWp/btsLjkeqSy4/jelESZmr59OKdAEQXt4RkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpGNWp/btsLjkeqSy4/jelESZmr59OKdAEQXt4RkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpGNWp/btsLjkeqSy4/jelESZmr59OKdAEQXt4RkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpGNWp%2FbtsLjkeqSy4%2FjelESZmr59OKdAEQXt4RkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;717&quot; height=&quot;639&quot; data-origin-width=&quot;717&quot; data-origin-height=&quot;639&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 IPv4는 도커 컨테이너의 IP 혹은 로드 밸런서가 바라보는 내부 요청을 전달할 타겟 주소들이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 중인 postgres 컨테이너 1개&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;my_databases_postgres.1 : 10.0.2.40/24&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 postgres 서비스의 IP 인터페이스는 VIP로 생성되며, 다음과 같이 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker service inspect my-databases_postgres&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;437&quot; data-origin-height=&quot;194&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDuAi0/btsLilSX3Ds/jizloDLH3QRxaE7i4P9DD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDuAi0/btsLilSX3Ds/jizloDLH3QRxaE7i4P9DD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDuAi0/btsLilSX3Ds/jizloDLH3QRxaE7i4P9DD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDuAi0%2FbtsLilSX3Ds%2FjizloDLH3QRxaE7i4P9DD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;437&quot; height=&quot;194&quot; data-origin-width=&quot;437&quot; data-origin-height=&quot;194&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VIP는 로드 밸런서에 의해 관리되며, VIP를 통한 연결은 postgres service 복제 노드로 전달된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker network ls를 통해 도커 스웜이 생성한 두 네트워크를 직접 확인 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;118&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2Y1Xw/btsLjrxzdQ4/2gqadGbdyeUE8s1UASdRR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2Y1Xw/btsLjrxzdQ4/2gqadGbdyeUE8s1UASdRR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2Y1Xw/btsLjrxzdQ4/2gqadGbdyeUE8s1UASdRR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2Y1Xw%2FbtsLjrxzdQ4%2F2gqadGbdyeUE8s1UASdRR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;658&quot; height=&quot;118&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;118&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;default 네트워크를 대체하려면 Compose 파일의 networks 엔트리에 명시하라&lt;/p&gt;
&lt;pre id=&quot;code_1734168772864&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;version: &quot;3.7&quot;
networks:
	foo:
		driver: overlay
volumes:
  pgdata:
services:
  postgres:
    image: dockerinaction/postgres:11-alpine
    volumes:
      - type: volume
        source: pgdata
        target: /var/lib/postgresql/data
    networks:
    	- foo
    environment:
      POSTGRES_PASSWORD: example
  
  mariadb:
    image: dockerinaction/mariadb:10-bionic
    networks:
    	- foo
    environment:
      MYSQL_ROOT_PASSWORD: example
    
  adminer:
    image: dockerinaction/adminer:4
    networks:
    	- foo
    ports:
      - 8080:8080
    deploy:
      replicas: 2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;요약&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스는 네트워크를 통해 제공되는 프로세스, 기능, 데이터를 의미하낟.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스웜과 같은 오케스트레이터는 유저가 설정한 상태를 충족시키기 위해 서비스, 볼륨, 네트워크를 유지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오케스트레이터는 복제, 복원, 배포, 헬스 체크, 롤백 기능을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목표 상태는 유저가 시스템으로 요구하는 달성해야할 상태를 의미하며, docker compose 파일로 작성 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;compose 파일은 YAML 형식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;compose 파일은 도커 서비스, 볼륨, 네트워크를 정의하며 스택을 만드는 정의서이다.&lt;/p&gt;</description>
      <category>Cloud/Docker</category>
      <author>로파이</author>
      <guid isPermaLink="true">https://narakit.tistory.com/315</guid>
      <comments>https://narakit.tistory.com/315#entry315comment</comments>
      <pubDate>Sat, 14 Dec 2024 16:35:14 +0900</pubDate>
    </item>
    <item>
      <title>Docker In Action 9. Public and private software distribution</title>
      <link>https://narakit.tistory.com/314</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공개 혹은 비공개 소프트웨어 배포&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;9.1 배포 방법 선택 하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 사항을 고려하여 개발 실정에 맞는 배포 방법을 선택하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;배포 스펙트럼&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1393&quot; data-origin-height=&quot;331&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/84Xsi/btsLc5uENTg/CVhKiyT64u3HkKdDXkW0e0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/84Xsi/btsLc5uENTg/CVhKiyT64u3HkKdDXkW0e0/img.png&quot; data-alt=&quot;배포 스펙트럼&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/84Xsi/btsLc5uENTg/CVhKiyT64u3HkKdDXkW0e0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F84Xsi%2FbtsLc5uENTg%2FCVhKiyT64u3HkKdDXkW0e0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1393&quot; height=&quot;331&quot; data-origin-width=&quot;1393&quot; data-origin-height=&quot;331&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;배포 스펙트럼&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왼쪽으로 기술될 수록 배포 방식은 단순하나 제한적인 방식으로 제공된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오른쪽으로 기술될 수록 배포 방식은 복잡하나 커스터 마이즈가 쉽게 되므로 유연하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;선택 기준&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포 방식을 선택함에 있어 기준점이 될 수 있는 요소를 나열 해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cost 비용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대게 낮은 비용이 최선의 선택이다. 프로젝트에 주어진 예산에 따라 결정된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visibility 가시성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 프로젝트는 공개되어서는 안된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transportation 전송&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포 환경의 네트워크 전송 속도와 대역폭은 배포 방식을 선택하는데 영향을 줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Longevity 지속 가능성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공개되지 않은 배포 방식을 공개된 방식으로 전환했을 때 문제 없이 진행될 것 인가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Availability 가용성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언제나 사용가능한 가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Access Control 접근 제어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 빌드 이미지를 다른 서드 파티에 의해 변경하거나 접근하는 것을 허용할 것인가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Integrity 무결성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신뢰성 있고 일관성 있는 배포 방식. 중간 자 공격과 같은 해킹에 영향이 없도록 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Confidentiality 비밀성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트가 허용하는 공개 범위 내에서 이미지가 배포되는 가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Expertise 전문성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도구에 대한 전문 지식을 요구하는가. 배포 방법이 복잡해질 수록 더 높은 전문성을 요하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;호스팅된 저장소를 통한 배포 hosted registries&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1429&quot; data-origin-height=&quot;364&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H5lwg/btsLbO8vslZ/B5piHRNkHBWmtgIPPKKu91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H5lwg/btsLbO8vslZ/B5piHRNkHBWmtgIPPKKu91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H5lwg/btsLbO8vslZ/B5piHRNkHBWmtgIPPKKu91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH5lwg%2FbtsLbO8vslZ%2FB5piHRNkHBWmtgIPPKKu91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1429&quot; height=&quot;364&quot; data-origin-width=&quot;1429&quot; data-origin-height=&quot;364&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker registries는 docker pull 명령어로 가져올 수 있는 이미지를 관리하는 저장소이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러한 이미 호스팅되고 있는 서비스를 통해 이미지를 배포하는 방식.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker Hub&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Quay.io&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 HelloWorld.df 를 작성하고 이미지를 빌드하여 docker registry에 배포한다.&lt;/p&gt;
&lt;pre id=&quot;code_1733746199313&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM busybox:latest
CMD echo 'Hello World!'&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1733746246430&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker image build -t &amp;lt;insert Docker Hub username&amp;gt;/hello-dockerfile -f HelloWorld.df .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;docker Hub username&amp;gt; 부분은 Docker Hub에 가입하여 생성한 username을 기입한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 cli에서 docker login을 통해 배포에 대한 인증을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 방식은 비용 효율적(무료)이며 모든 사람에게 공개된 레지스트리를 사용한다. 접근 제어 또한 용이한데, 공개된 사용자들에게는 읽기 가능하고 개인, 조직 단위로 이미지를 업데이트할 수 있는 권한을 가진다. 데이터는 호스팅된 서비스를 통해 일관성있게 유지되며 큰 전문성 없이 유지보수가 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 비공개용 프로젝트에는 부적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비공개 저장소&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비공개 저장소는 공개 저장소와 모든 면이 비슷하다. docker registry API는 공개와 비공개 저장소에 대한 구분을 거의 하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차이점은 docker image pull 이나 docker container run 을 비공개 저장소의 이미지를 지정할 때, docker login과 같은 인증 절차를 필요로 한다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공개되지 않기 때문에 비밀성에 대한 충족 사항이 만족된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;개인 저장소를 도입 private registries&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1393&quot; data-origin-height=&quot;364&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uItsh/btsLdbuJonj/y6XfnIvepkrg1ojYUPn2tK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uItsh/btsLdbuJonj/y6XfnIvepkrg1ojYUPn2tK/img.png&quot; data-alt=&quot;개인 저장소&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uItsh/btsLdbuJonj/y6XfnIvepkrg1ojYUPn2tK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuItsh%2FbtsLdbuJonj%2Fy6XfnIvepkrg1ojYUPn2tK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1393&quot; height=&quot;364&quot; data-origin-width=&quot;1393&quot; data-origin-height=&quot;364&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;개인 저장소&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker registry를 격리된 네트워크에서 서비스하여 직접 저장소 서비스를 구축하고 해당 저장소에 이미지를 배포하는 방식이다. Docker registry는 Apache License 2 하에 배포되는 오픈 소스이다. Docker registry를 직접 구축하여 서비스하여 비용 절감과 서비스 구현 비용을 줄일 수 있을 것 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 배포에 대한 지엽적인 특징을 가지며 호스팅된 서비스와 이미지 소스 배포 사이의 중간 특징을 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레지스트리에 대한 커스터 마이징과 환경에 맞는 배포 설정을 가능하게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 레지스트리에서 이미지를 받아 로컬 저장소에 푸시하는 예시&lt;/p&gt;
&lt;pre id=&quot;code_1733749006402&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker image pull dockerinaction/ch9_registry_bound
docker image ls -f &quot;label=dia_exercise=ch9_registry_bound&quot;
docker image tag dockerinaction/ch9_registry_bound localhost:3000/dockerinaction/ch9_registry_bound
docker image push localhost:5000/dockerinaction/ch9_registry_bound&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;개인 저장소는 localhost:5000에서 서비스 중이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;직접 수동으로 이미지 배포&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1393&quot; data-origin-height=&quot;357&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXq5lw/btsLbMXaIQe/tBaK3PAJJTV59xANCSGku0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXq5lw/btsLbMXaIQe/tBaK3PAJJTV59xANCSGku0/img.png&quot; data-alt=&quot;고유 배포 인프라가 있는 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXq5lw/btsLbMXaIQe/tBaK3PAJJTV59xANCSGku0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXq5lw%2FbtsLbMXaIQe%2FtBaK3PAJJTV59xANCSGku0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1393&quot; height=&quot;357&quot; data-origin-width=&quot;1393&quot; data-origin-height=&quot;357&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;고유 배포 인프라가 있는 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FTP 파일전송 형태나 HTTPS와 같은 웹 서비스로 직접 이미지를 다운로드하여 배포하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 이미지를 사용하여 배포하는 경우 그리고 중간 파일 서버를 통해 이미지를 배포하는 방식을 예로 들 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1896&quot; data-origin-height=&quot;665&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beCED9/btsLc1sqbY2/yqBMjqmltnBj6fmpZrUet0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beCED9/btsLc1sqbY2/yqBMjqmltnBj6fmpZrUet0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beCED9/btsLc1sqbY2/yqBMjqmltnBj6fmpZrUet0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeCED9%2FbtsLc1sqbY2%2FyqBMjqmltnBj6fmpZrUet0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1896&quot; height=&quot;665&quot; data-origin-width=&quot;1896&quot; data-origin-height=&quot;665&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지는 export를 통해 tar 파일로 내보내지고 FTP 서버를 통해 배포되는 인프라에 전송된다. 전송된 파일은 다시 이미지로 import되어 컨테이너를 실행하는 데 쓰이게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이미지 소스-배포 워크플로우&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;351&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/doITF9/btsLbZaX1E2/BAQBRNnCKxr7BF9Gzd0C30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/doITF9/btsLbZaX1E2/BAQBRNnCKxr7BF9Gzd0C30/img.png&quot; data-alt=&quot;기존 배포 인프라를 사용하여 이미지 소스를 배포&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/doITF9/btsLbZaX1E2/BAQBRNnCKxr7BF9Gzd0C30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdoITF9%2FbtsLbZaX1E2%2FBAQBRNnCKxr7BF9Gzd0C30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1408&quot; height=&quot;351&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;351&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;기존 배포 인프라를 사용하여 이미지 소스를 배포&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Git과 같은 버전 컨트롤 시스템을 사용하여 소스로 부터 직접 빌드 및 배포를 하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) git 저장소로 부터 소스를 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 이미지를 빌드 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) 빌드한 이미지를 저장소로 내보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;요약&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 사항을 고려하여 배포 방식을 선택하여 프로젝트에 도입하라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hosted public repositories는 프로젝트 가시성, 무료, 낮은 전문성 요구를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hosted private repositores는 소규모 프로젝트에 적합하고 접근 제어를 용이하게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FTP와 같은 file-sharing system을 사용하면 이미지를 파일로서 배포할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 소스 배포는 유연하나 복잡하게 만들려면 얼마든지 가능하다. 기존에 있는 인프라를 사용하여 배포한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Cloud/Docker</category>
      <author>로파이</author>
      <guid isPermaLink="true">https://narakit.tistory.com/314</guid>
      <comments>https://narakit.tistory.com/314#entry314comment</comments>
      <pubDate>Mon, 9 Dec 2024 22:31:52 +0900</pubDate>
    </item>
    <item>
      <title>Docker In Action 8. Building image automaticall with Dockerfiles</title>
      <link>https://narakit.tistory.com/313</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile 이란&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker 이미지를 빌드하는 데 쓰이는 지시사항들의 모음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8.1 Dockerfile을 이용하여 ubuntu-git 빌드하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile이라는 이름으로 파일을 만들고 다음 내용을 작성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1733655756940&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM ubuntu:latest
LABEL maintainer=&quot;dia@allingeek.com&quot;
RUN apt-get update &amp;amp;&amp;amp; apt-get install -y git
ENTRYPOINT [&quot;git&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 빌드하기&lt;/p&gt;
&lt;pre id=&quot;code_1733655786665&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker buildx build --tag ubuntu-git:auto .&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;88&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tJDzX/btsLaI1kPFK/wPfa2jjdxQM66m8vDRLdjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tJDzX/btsLaI1kPFK/wPfa2jjdxQM66m8vDRLdjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tJDzX/btsLaI1kPFK/wPfa2jjdxQM66m8vDRLdjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtJDzX%2FbtsLaI1kPFK%2FwPfa2jjdxQM66m8vDRLdjk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;652&quot; height=&quot;88&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;88&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;auto 태그로 된 이미지가 존재하는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- FROM : 기본이 되는 이미지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- LABEL : key=value 형태로 label을 부여한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- RUN : 컨테이너를 실행한 후 프로그램 실행 명령어를 나열한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- ENTRYPOINT : 프로그램 진입점을 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile에 새로운 내용을 추가한다면, 새로운 레이어가 추가된 이미지가 빌드된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker 이미지 빌드 시스템은 캐싱을 이용하여 단계별 레이어가 이미 빌드된 캐시를 사용할 수 있다면, 빠르게 빌드할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1733656021627&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM ubuntu:latest
LABEL maintainer=&quot;dia@allingeek.com&quot;
RUN apt-get update &amp;amp;&amp;amp; apt-get install -y git
ENTRYPOINT [&quot;git&quot;]
RUN this will not work&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Run this will not work를 추가해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;356&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/51Z27/btsLbWquBgd/QDM3vlT22CYgIVUSNKXOyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/51Z27/btsLbWquBgd/QDM3vlT22CYgIVUSNKXOyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/51Z27/btsLbWquBgd/QDM3vlT22CYgIVUSNKXOyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F51Z27%2FbtsLbWquBgd%2FQDM3vlT22CYgIVUSNKXOyk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;904&quot; height=&quot;356&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;356&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 그 전 레이어에 대한 캐싱을 사용하기 때문에 이전 빌드보다 빠르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- --no-cache 옵션을 사용하면 캐시 사용 없이 빌드 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8.2 도커 파일 핵심&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메타 데이터 지시사항&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.dockerignore 파일을 작성하면 해당 리스트에 있는 파일들은 이미지 빌드 시 컨테이너 내부로 복사를 방지한다.&lt;/p&gt;
&lt;pre id=&quot;code_1733656192001&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.dockerignore

mailer-base.df
mailer-logging.df
mailer-live.df&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mailer-base.df 예시&lt;/p&gt;
&lt;pre id=&quot;code_1733656553970&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM debian:buster-20190910
LABEL maintainer=&quot;dia@allingeek.com&quot;
RUN groupadd -r -g 2200 example &amp;amp;&amp;amp; \
    useradd -rM -g example -u 2200 example
ENV APPROOT=&quot;/app&quot;\
    APP=&quot;mailer.sh&quot; \
    VERSION=&quot;0.6&quot;
LABEL base.name=&quot;Mailer Archetype&quot; \
      base.version=&quot;${VERSION}&quot;
WORKDIR $APPROOT
ADD . $APPROOT
ENTRYPOINT [&quot;/app/mailer.sh&quot;]
EXPOSE 33333&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- LABEL은 이미지나 컨테이너에 메타 데이터를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- WORKDIR : 기본 작업 디렉토리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- EXPOSE : 도커 컨테이너 외부로 공개할 포트 번호를 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- ENTRYPOINT : 프로그램 진입점을 의미하며 컨테이너 내부에서는 /bin/sh -c 'exec ./mailer.sh' 를 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;ENTRPOINT를 명시할 경우 CMD나 docker container run을 통해 명령어를 주입할 경우 무시되므로 주의.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mailer-base:0.6으로 이미지를 빌드해두자.&lt;/p&gt;
&lt;pre id=&quot;code_1733657740732&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker buildx build -t dockerinaction/mailer-base:0.6 -f mailer-base.df .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Filesystem 관련 사항&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로깅 프로그램을 위한 mailer-logging.df 도커 파일 예시&lt;/p&gt;
&lt;pre id=&quot;code_1733656896481&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM dockerinaction/mailer-base:0.6
RUN apt-get update &amp;amp;&amp;amp; \
    apt-get install -y netcat
COPY [&quot;./log-impl&quot;, &quot;${APPROOT}&quot;]
RUN chmod a+x ${APPROOT}/${APP} &amp;amp;&amp;amp; \
    chown example:example /var/log
USER example:example
VOLUME [&quot;/var/log&quot;]
CMD [&quot;/var/log/mailer.log&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;COPY : 이미지 빌드 파일 시스템으로 부터 컨테이너 내부로 복사&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VOLUME : 이미지 파일 시스템에 볼륨을 생성 및 할당&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CMD : ENTRYPOINT와 유사하나 커스텀한 명령어를 실행할 경우 지정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;log-impl 폴더를 만들고 하위에 mailer.sh 쉘 스크립트를 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mailer-base 이미지에 지정한 mailer.sh 스크립트 실행을 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1733657827722&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker image build -t dockerinaction/mailer-logging -f mailer-logging.df .&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;929&quot; data-origin-height=&quot;292&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIGtJx/btsLaYbLSdl/YkbZYSkKhZ1I9DdAVPjXG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIGtJx/btsLaYbLSdl/YkbZYSkKhZ1I9DdAVPjXG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIGtJx/btsLaYbLSdl/YkbZYSkKhZ1I9DdAVPjXG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIGtJx%2FbtsLaYbLSdl%2FYkbZYSkKhZ1I9DdAVPjXG0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;929&quot; height=&quot;292&quot; data-origin-width=&quot;929&quot; data-origin-height=&quot;292&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너를 빌드한 이미지 mailer-logging로부터 실행한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;873&quot; data-origin-height=&quot;40&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5Jh5P/btsLalMkaHR/iW4rbBprc4pgUoXuXfd4wK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5Jh5P/btsLalMkaHR/iW4rbBprc4pgUoXuXfd4wK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5Jh5P/btsLalMkaHR/iW4rbBprc4pgUoXuXfd4wK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5Jh5P%2FbtsLalMkaHR%2FiW4rbBprc4pgUoXuXfd4wK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;873&quot; height=&quot;40&quot; data-origin-width=&quot;873&quot; data-origin-height=&quot;40&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mailer-live.df 작성하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Amazon Web Servcice의 Simple Email Service를 이용하는 mailer-live.df를 작성해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1733658273521&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM dockerinaction/mailer-base:0.6
ADD [&quot;./live-impl&quot;, &quot;${APPROOT}&quot;]
RUN apt-get update &amp;amp;&amp;amp; \
    apt-get install -y curl netcat python &amp;amp;&amp;amp; \
    curl &quot;https://bootstrap.pypa.io/get-pip.py&quot; -i &quot;get-pip.py&quot; &amp;amp;&amp;amp; \
    python get-pip.py &amp;amp;&amp;amp; \
    pip install awscli &amp;amp;&amp;amp; \
    rm get-pip.py &amp;amp;&amp;amp; \
    chmod a+x &quot;${APPROOT}/${APP}&quot;
USER example:example
CMD [&quot;mailer@dockerianction.com&quot;, &quot;pager@dockerinaction.com&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- ADD : COPY와 유사하지만 URL을 지정할 경우 원격지 소스로 부터 파일을 받아 복사한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 live-impl 폴더를 생성하고 적절한 SES API를 호출하는 mailer.sh를 작성한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8.3 다운 스트림 빌드 타임 동작 주입하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1733658482539&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ONBUILD COPY [&quot;.&quot;, &quot;/var/myapp&quot;]
ONBUILD RUN go build /var/myapp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ONBUILD 동작은 해당 이미지를 기본으로 하는 다운 스트림 이미지가 실제로 빌드될 때, 동작하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;upstream.df&lt;/p&gt;
&lt;pre id=&quot;code_1733658735525&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM busybox:latest
WORKDIR /app
RUN touch /app/base-evidence
ONBUILD RUN ls -al /app&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1733658754187&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; docker buildx build -t dockerinaction/ch8_onbuild -f upstream.df .&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;867&quot; data-origin-height=&quot;242&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tzqgS/btsLbMPer0o/ZrGfQShkyQF901KLHPPEDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tzqgS/btsLbMPer0o/ZrGfQShkyQF901KLHPPEDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tzqgS/btsLbMPer0o/ZrGfQShkyQF901KLHPPEDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtzqgS%2FbtsLbMPer0o%2FZrGfQShkyQF901KLHPPEDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;867&quot; height=&quot;242&quot; data-origin-width=&quot;867&quot; data-origin-height=&quot;242&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RUN ls -al /app 을 실행하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;downstream.df&lt;/p&gt;
&lt;pre id=&quot;code_1733658914700&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM dockerinaction/ch8_onbuild
RUN touch downstream-evidence
RUN ls -al&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1733658952652&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker image build -t dockerinaction/ch8_onbuild_down -f downstream.df .&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4jjQ2/btsLapuvBNK/gK2m8skjGuiTHTcc2A8TJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4jjQ2/btsLapuvBNK/gK2m8skjGuiTHTcc2A8TJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4jjQ2/btsLapuvBNK/gK2m8skjGuiTHTcc2A8TJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4jjQ2%2FbtsLapuvBNK%2FgK2m8skjGuiTHTcc2A8TJ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;858&quot; height=&quot;264&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ch8_onbuild_down 이미지를 빌드할 때 기본이 되는 이미지를 가져온 직후 ONBUILD로 지정한 지시사항들이 적용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8.4 관리 가능한 도커 파일 만들기&lt;/p&gt;
&lt;pre id=&quot;code_1733659199009&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM debian:buster-20190910
ARG VERSION=unknown
LABEL maintainer=&quot;dia@allingeek.com&quot;
RUN groupadd -r -g 2200 example &amp;amp;&amp;amp; \
    useradd -rM -g example -u 2200 example
ENV APPROOT=&quot;/app&quot;\
    APP=&quot;mailer.sh&quot; \
    VERSION=&quot;0.6&quot;
LABEL base.name=&quot;Mailer Archetype&quot; \
      base.version=&quot;${VERSION}&quot;
WORKDIR $APPROOT
ADD . $APPROOT
ENTRYPOINT [&quot;/app/mailer.sh&quot;]
EXPOSE 33333&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ARG로 지정한 변수는 이미지를 빌드할 때 결정되는 값이다. 실제 빌드 시점에 결정되는 값에 쓰인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1733659239398&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;version=0.6;
docker buildx build -t dockerinaction/mailer-base:${version} \
	-f mailer-base.df \
    --build-arg VERSION=${version} \
    .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;멀티 스테이지 빌드 옵션&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 도커 파일안에 빌드 stage를 둘 이상 지정하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build와 runtime 빌드 stage가 있다면, runtime 빌드는 build stage에서 정의된 값을 가져올 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1733659507676&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Builder stage
FROM go-lang:1-alpine as builder

# Install CA
RUN apk update &amp;amp;&amp;amp; apk add ca-certificates

# Copy source into Builder
ENV HTTP_CLIENT_SRC=$GOPATH/src/dia/http-client/
COPY . $HTTP_CLIENT_SRC
WORKDIR $HTTP_CLIENT_SRC

# Build HTTP client
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
    go build -v -o /go/bin/http-client

# Runtime stage
FROM scratch as runtime
ENV PATH =&quot;/bin&quot;

COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certicifates.crt
COPY --from=builder /go/bin/http-client /http-client
ENTRYPOINT [&quot;/http-client&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;builder 스테이지에서는 HTTPS 서비스를 위한 CA 인증서를 설치하고 http-client 실행 파일을 빌드한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;runtime stage는 아무런 파일 시스템이 존재하지 않는 &quot;scratch&quot; 레이어에서 CA 인증서와 실행 파일만 복사하여 HTTPS 서비스를 실행하기 위한 이밎 빌드 단계 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8.5 시작 스크립트 및 멀티 프로세스 컨테이너&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이른 환경 검증하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스를 실행하기 전에 유효하지 않은 환경 설정이 발견되면, 더 진행하지 않고 빠른 종료를 권장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러한 시작 프로세스는 다음과 같은 값을 검증한 뒤, 원래 실행 파일을 실행하는 단계로 구성되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 별칭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 환경 변수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 시크릿&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 네트워크 접근 권한&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 네트워크 포트 사용 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 볼륨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 현재 유저&lt;/p&gt;
&lt;pre id=&quot;code_1733659974713&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash

set -e
if [ -n &quot;$WEB_PORT80_TCP&quot;]; then
    if [ -z $&quot;WEB_HOST&quot; ]; then
        WEB_HOST='web'
    else
        echo &amp;gt;$2 '[WARN]: Linked container, &quot;web&quot; overriden by $WEB_HOST.'
        echo &amp;gt;$2 &quot;===&amp;gt; Connecting to WEB_HOST ($WEB_HOST)&quot;
    fi
fi

if [ -z &quot;$WEB_HOST&quot; ]; then
    echo &amp;gt;$2 '[ERROR] : specify container to link;'
    exit 1
fi

# time to run original process
exec &quot;$@&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기화 프로세스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;init process는 다중 프로그램을 관리하고, 댕글링된 프로세스를 정리하며 프로세스를 모니터링하고 실패 감지 시 자동 재시작 옵션을 제공하는 데 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1733660184352&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container run -it --init alpine:3.6 nc -l -p 3000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--init 옵션은 init 프로세스를 컨테이너 내부에서 실행할 수 있게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Cloud/Docker</category>
      <author>로파이</author>
      <guid isPermaLink="true">https://narakit.tistory.com/313</guid>
      <comments>https://narakit.tistory.com/313#entry313comment</comments>
      <pubDate>Sun, 8 Dec 2024 21:20:25 +0900</pubDate>
    </item>
    <item>
      <title>Docker In Action 7. Packaging software in images</title>
      <link>https://narakit.tistory.com/312</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 빌드를 통한 소프트웨어 패키징&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7.1 컨테이너로 부터 도커 이미지 빌드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커에서 union filesystem (UFS)기반의 마운트를 통해 컨테이너의 파일 시스템 기능을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7.1.1 Packaging &quot;Hello, World&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시) 나만의 이미지 빌드&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 기존 이미지로 컨테이너를 실행하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 컨테이너의 파일 시스템에 변경 사항을 만들자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 변경 사항을 이미지로 커밋 한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1733488109851&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. ubuntu:latest 이미지를 가져와 컨테이너를 실행한 후 HelloWorld 파일을 만든다.
docker container run --name hw_container ubuntu:latest touch /HelloWorld

// 2. 현재 컨테이너를 이미지로 커밋 하자
docker container commit hw_container hw_image

// 3. 기존 컨테이너를 정리
docker container rm -vf hw_container

// 4. 새로운 컨테이너에서 파일을 확인하자
docker container run --rm hw_image ls -l /HelloWorld&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7.1.2 Git 기반 패키징 준비하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Git 도구를 기존 이미지에 설치해서 패키징을 해보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 도커 컨테이너를 하나 실행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1733488524592&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container run -it --name image-dev ubuntu:latest /bin/bash&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 해당 컨테이너에 git을 설치한다.&lt;/p&gt;
&lt;pre id=&quot;code_1733488591471&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apt-get update
apt-get -y install git

git version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;exit을 통해 컨테이너 shell을 빠져 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7.1.3 파일 시스템 변경 사항을 확인해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 파일 추가&lt;/p&gt;
&lt;pre id=&quot;code_1733488915274&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container run --name tweak-a busybox:latest touch /HelloWorld
docker container diff tweak-a&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;258&quot; data-origin-height=&quot;21&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x67D9/btsK9VzROqG/kSTP3Qk5FND1u7LfimoBwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x67D9/btsK9VzROqG/kSTP3Qk5FND1u7LfimoBwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x67D9/btsK9VzROqG/kSTP3Qk5FND1u7LfimoBwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx67D9%2FbtsK9VzROqG%2FkSTP3Qk5FND1u7LfimoBwK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;258&quot; height=&quot;21&quot; data-origin-width=&quot;258&quot; data-origin-height=&quot;21&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;- A : HelloWorld 파일이 추가되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 파일 삭제&lt;/p&gt;
&lt;pre id=&quot;code_1733489002530&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container run --name tweak-d busybox:latest rm /bin/vi
docker container diff tweak-d&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;188&quot; data-origin-height=&quot;32&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blo1kB/btsLaDejJ1b/2PUcjOXBY9cWuv3QV0Wfsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blo1kB/btsLaDejJ1b/2PUcjOXBY9cWuv3QV0Wfsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blo1kB/btsLaDejJ1b/2PUcjOXBY9cWuv3QV0Wfsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fblo1kB%2FbtsLaDejJ1b%2F2PUcjOXBY9cWuv3QV0Wfsk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;188&quot; height=&quot;32&quot; data-origin-width=&quot;188&quot; data-origin-height=&quot;32&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;- C: /bin 폴더에 변경 사항 존재&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;- D: /bin/vi 폴더가 삭제되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) 변경사항 만들기&lt;/p&gt;
&lt;pre id=&quot;code_1733489117566&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container run --name tweak-c busybox:latest touch /bin/vi
docker container diff tweak-c&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;80&quot; data-origin-height=&quot;31&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HRaeq/btsLbB0Vpoy/JtD1Pydnhf5ki0ij8RlFD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HRaeq/btsLbB0Vpoy/JtD1Pydnhf5ki0ij8RlFD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HRaeq/btsLbB0Vpoy/JtD1Pydnhf5ki0ij8RlFD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHRaeq%2FbtsLbB0Vpoy%2FJtD1Pydnhf5ki0ij8RlFD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;80&quot; height=&quot;31&quot; data-origin-width=&quot;80&quot; data-origin-height=&quot;31&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;- C: /bin 폴더에 변경 사항 존재&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;- D: /bin/vi 폴더가 &lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;변경 사항 존재 (접근 시간 기록 등)&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7.1.4 새로운 이미지 커밋&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;commit 명령어&lt;/b&gt; 사용하여 컨테이너로 부터 이미지 만들기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-a 옵션으로 저자 및 -m 메시지를 지정한다.&lt;/p&gt;
&lt;pre id=&quot;code_1733489806860&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container commit -a &quot;@dockerinaction&quot; -m &quot;Added git&quot; image-dev ubuntu-git&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker images {image_name} 이미지 확인하기&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;61&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXfyRv/btsLblcY44X/27pwyu1pjXrufMA1VLuZUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXfyRv/btsLblcY44X/27pwyu1pjXrufMA1VLuZUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXfyRv/btsLblcY44X/27pwyu1pjXrufMA1VLuZUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXfyRv%2FbtsLblcY44X%2F27pwyu1pjXrufMA1VLuZUk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;486&quot; height=&quot;61&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;61&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 이미지에 git 도구가 있는 지 확인&lt;/p&gt;
&lt;pre id=&quot;code_1733489921145&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container run --rm ubuntu-git git version&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;554&quot; data-origin-height=&quot;30&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6BiKz/btsLbaie7Cc/eQKfn7YAvZbALQ7SWAukSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6BiKz/btsLbaie7Cc/eQKfn7YAvZbALQ7SWAukSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6BiKz/btsLbaie7Cc/eQKfn7YAvZbALQ7SWAukSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6BiKz%2FbtsLbaie7Cc%2FeQKfn7YAvZbALQ7SWAukSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;554&quot; height=&quot;30&quot; data-origin-width=&quot;554&quot; data-origin-height=&quot;30&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;entrypoint 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 옵션을 지정하는 경우, 컨테이너 시작 시 해당 명령어를 기본으로 실행한다. 말 그대로 프로그램 진입점을 의미.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1733490249107&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container run --name cmd-git --entrypoint git ubuntu-git&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 컨테이너를 이미지로 커밋하고 다시 실행해보자. 지정한 entrypoint (git) 가 적용된다.&lt;/p&gt;
&lt;pre id=&quot;code_1733490692878&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container commit -m &quot;Set CMD git&quot; -a &quot;@dockerinaction&quot; cmd-git ubuntu-git

docker contaienr rm -vf cmd-git

docker container run --name cmd-git ubuntu-git version&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;34&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYhL3a/btsLbQjfoU0/vk4fbZFk4iDw6kgGW2dAtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYhL3a/btsLbQjfoU0/vk4fbZFk4iDw6kgGW2dAtk/img.png&quot; data-alt=&quot;git version&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYhL3a/btsLbQjfoU0/vk4fbZFk4iDw6kgGW2dAtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYhL3a%2FbtsLbQjfoU0%2Fvk4fbZFk4iDw6kgGW2dAtk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;582&quot; height=&quot;34&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;34&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;git version&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7.1.5 이미지 속성 구성하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;commit을 한다는 것은 새로운 layer를 추가하는 것과 같다. commit으로 파일 시스템도 포함하지만 다음 실행 문맥과 관련된 메타 데이터도 layer에 포함된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;- 모든 환경 변수&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;- 기본 작업 폴더 경로&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;- 공개 포트&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;- 볼륨 정의&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;- 컨테이너 진입점&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;- 실행 명령어와 인수&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 예시는 환경 변수를 이미지 레이어에 추가하는 예시이다.&lt;/p&gt;
&lt;pre id=&quot;code_1733491262962&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container run --name rich-image-example -e ENV_EXAMPLE1=Rich -e ENV_EXAMPLE2=Example busybox:latest

docker conatiner commit rich-image-example rie

docker container run --rm rie /bin/sh -c &quot;echo \$ENV_EXAMPLE1 \$ENV_EXAMPLE2&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;574&quot; data-origin-height=&quot;118&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PUKmN/btsK9kNBYM0/nFKu7IQSt0OP0Dtc9wAkB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PUKmN/btsK9kNBYM0/nFKu7IQSt0OP0Dtc9wAkB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PUKmN/btsK9kNBYM0/nFKu7IQSt0OP0Dtc9wAkB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPUKmN%2FbtsK9kNBYM0%2FnFKu7IQSt0OP0Dtc9wAkB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;574&quot; height=&quot;118&quot; data-origin-width=&quot;574&quot; data-origin-height=&quot;118&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rie 라는 이미지에는 ENV_EXAMPLE1, ENV_EXAMPLE2 환경 변수가 기록된다.&lt;/p&gt;
&lt;pre id=&quot;code_1733491773169&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container run --name rich-image-example-2 --entrypoint &quot;/bin/sh&quot; rie -c &quot;echo \$ENV_EXAMPLE1 \$ENV_EXAMPLE2&quot;

docker container commit rich-image-example-2 rie

docker contianer run --rm rie&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;entrypoint를 &quot;/bin/sh&quot; 로 지정하고 명령어와 인수를 지정하는 -c 옵션을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 이미지에는 entrypoint와 명령어 메타 정보가 기록된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7.2 Docker 이미지와 레이어 더 알아보기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커는 union fileysystem을 쓰기에 이미지 저자가 다음 중요사항을 알아야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 저자는 파일의 추가, 삭제, 변경 내역은 이미지에 영향을 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 저자는 레이어와 이미지와 관련된 레이어, 레파지토리, 태그와 연관 관계를 알아야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mychange 파일을 생성하는 케이스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ubuntu:latest 이미지로부터 mod_ubuntu 컨테이너를 실행하고 mychage 파일을 만든다.&lt;/p&gt;
&lt;pre id=&quot;code_1733641335689&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker contaienr run --name mod_ubuntu ubuntu:latest touch /mychange&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레이어 관점에서 동작&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1488&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baLdaS/btsLaK5ShiW/1z6y6JWpCPAL1iH2t7nqPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baLdaS/btsLaK5ShiW/1z6y6JWpCPAL1iH2t7nqPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baLdaS/btsLaK5ShiW/1z6y6JWpCPAL1iH2t7nqPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaLdaS%2FbtsLaK5ShiW%2F1z6y6JWpCPAL1iH2t7nqPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;604&quot; height=&quot;201&quot; data-origin-width=&quot;1488&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원본 이미지로 부터 새로운 변경 사항은 새로운 레이어 위에 쓰여진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 추가, 삭제, 변경 모두 해당된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;413&quot; data-origin-height=&quot;31&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sFSYZ/btsLbRiCorK/k8Aw8F1W8y3DNUjKiFBNPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sFSYZ/btsLbRiCorK/k8Aw8F1W8y3DNUjKiFBNPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sFSYZ/btsLbRiCorK/k8Aw8F1W8y3DNUjKiFBNPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsFSYZ%2FbtsLbRiCorK%2Fk8Aw8F1W8y3DNUjKiFBNPk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;413&quot; height=&quot;31&quot; data-origin-width=&quot;413&quot; data-origin-height=&quot;31&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7.2.2 images, layers, repositories, and tags&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;images&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;union fileysystem은 레이어를 쌓으면서 만들어지며 항상 새로운 레이어가 위에 쌓인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;현재까지의 변경사항으로 이미지 만들기&lt;/p&gt;
&lt;pre id=&quot;code_1733641728875&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container commit mod_ubuntu&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;519&quot; data-origin-height=&quot;37&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/puwme/btsLaJMFFRM/LnRYKEHXIwMAvLJYR8UgFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/puwme/btsLaJMFFRM/LnRYKEHXIwMAvLJYR8UgFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/puwme/btsLaJMFFRM/LnRYKEHXIwMAvLJYR8UgFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpuwme%2FbtsLaJMFFRM%2FLnRYKEHXIwMAvLJYR8UgFK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;519&quot; height=&quot;37&quot; data-origin-width=&quot;519&quot; data-origin-height=&quot;37&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 새로운 이미지 Id는 위와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;repository&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;repository에서 이미지 주소는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1733641872019&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;${registry_host}/${user_or_organization_name}/${short_name}
//docker.io/dockerinaction/ch3_hello_registry&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 이 주소로 이미지를 pull 한다면, 항상 short_name에 해당하는 이미지의 latest 태그 버전을 가져온다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;994&quot; data-origin-height=&quot;798&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0mex9/btsLb0GpdZn/EwoKLvMN522tJLQrdmAqH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0mex9/btsLb0GpdZn/EwoKLvMN522tJLQrdmAqH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0mex9/btsLb0GpdZn/EwoKLvMN522tJLQrdmAqH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0mex9%2FbtsLb0GpdZn%2FEwoKLvMN522tJLQrdmAqH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;452&quot; height=&quot;363&quot; data-origin-width=&quot;994&quot; data-origin-height=&quot;798&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;태그와 함께 이미지 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mod_ubuntu 컨테이너를 mytag 태그에 해당하는 myuser/myfirst_repo 이미지로 만든다.&lt;/p&gt;
&lt;pre id=&quot;code_1733642475213&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container commit mod_ubuntu myuser/myfirstrepo:mytag&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 이미지를 latest 태그로 만든다. (태그 부분이 생략되면 항상 latest)&lt;/p&gt;
&lt;pre id=&quot;code_1733642544015&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker tag myuser/myfirstrepo:mytag myuser/mod_ubuntu&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7.2.3 이미지 크기와 레이어 제한 관리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;latest 태그를 다시 (2.7) 태그한다.&lt;/p&gt;
&lt;pre id=&quot;code_1733642663225&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker image tag ubuntu-git:latest ubuntu-git:2.7&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ubuntu-git에서 git을 제거한 버전으로 이미지를 빌드하고 latest 태그를 부여한다.&lt;/p&gt;
&lt;pre id=&quot;code_1733642834230&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container run --name image-dev2 --entrypoint /bin/bash ubuntu-git:latest -c &quot;apt-get remove -y git&quot;
docker container commit image-dev2 ubuntu-git:removed
docker image tag ubuntu-git:removed ubuntu-git:latest
docker images &quot;ubuntu-git&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;436&quot; data-origin-height=&quot;73&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o2eLW/btsK9UOXnIs/I21XYIsfpG8v9qqnKCjhjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o2eLW/btsK9UOXnIs/I21XYIsfpG8v9qqnKCjhjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o2eLW/btsK9UOXnIs/I21XYIsfpG8v9qqnKCjhjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo2eLW%2FbtsK9UOXnIs%2FI21XYIsfpG8v9qqnKCjhjk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;436&quot; height=&quot;73&quot; data-origin-width=&quot;436&quot; data-origin-height=&quot;73&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최신 latest 버전에서 이미지 크기가 증가한 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UFS는 레이어에 파일을 삭제한 사실을 기록하기 때문에 전체 이미지 크기는 변경사항이 많을 수록 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;docker image history로 이미지의 변경 내역을 알아본다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 정보가 제공된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 축약된 layer ID&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 레이어 나이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 생성하는 컨테이너의 초기 명령어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 레이어의 총 파일 사이즈&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;157&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bu2838/btsLbQD3CgQ/koLfzzKuOHXXqD09ewaPM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bu2838/btsLbQD3CgQ/koLfzzKuOHXXqD09ewaPM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bu2838/btsLbQD3CgQ/koLfzzKuOHXXqD09ewaPM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbu2838%2FbtsLbQD3CgQ%2FkoLfzzKuOHXXqD09ewaPM0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;721&quot; height=&quot;157&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;157&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker save로 이미지를 TAR 파일로 내보내서 다시 import 한 다면 이미지 사이즈가 줄어들 수 는 있으나, 이미지 메타 정보를 잃기 때문에 좋은 방법은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브랜치 방법; 모든 변경사항을 되돌릴 수 있는 이전의 버전으로 돌아가서 이미지를 새로 만드는 것이다. ubuntu-git으로부터 git을 제거하였다면 ubuntu:latest 이미지에서 브랜칭을 하여 목적에 맞는 새로운 이미지를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7.3 Exporting and importing flat filysystems&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지를 파일로 내보내어 컨테이너나 UFS에 벗어난 문맥에서 관리하는 방법이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker container export ${컨테이너 이름}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어는 컨테이너를 tar 파일로 내보내는 명령어이다.&lt;/p&gt;
&lt;pre id=&quot;code_1733643812741&quot; class=&quot;cmake&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;docker container create --name export-test dockerinaction/ch7:packed:latest ./echo For Export
docker container export --output contents.tar export-test
docker container rm export-test
tar -tf contents.tar&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;268&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cU3qXB/btsLbNtL48L/0clKszZJMJBTv8RyHCt8Z0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cU3qXB/btsLbNtL48L/0clKszZJMJBTv8RyHCt8Z0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cU3qXB/btsLbNtL48L/0clKszZJMJBTv8RyHCt8Z0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcU3qXB%2FbtsLbNtL48L%2F0clKszZJMJBTv8RyHCt8Z0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;644&quot; height=&quot;268&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실행 파일을 빌드한 이미지를 만들고 컨테이너로 실행하기 (hostsystem linux only)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 go 내용을 빌드해본다.&lt;/p&gt;
&lt;pre id=&quot;code_1733644163008&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package main
import &quot;fmt&quot;

func main() {
	fmt.Println(&quot;hello, world!&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마운트한 파일을 빌드하는 다음과 같이 컨테이너를 실행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1733644292345&quot; class=&quot;bash&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;docker container run --rm -v &quot;$(pwd)&quot;:/usr/src/hello -w /usr/src/hello golang:1.9 go build -v&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tar 명령어로 압축 파일을 만든다.&lt;/p&gt;
&lt;pre id=&quot;code_1733644336917&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tar -cf static_hello.tar hello&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 entrypoint를 지닌 docker_inaction/ch7_static 이미지로 만든다.&lt;/p&gt;
&lt;pre id=&quot;code_1733645245379&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker import -c &quot;ENTRYPOINT [\&quot;/hello\&quot;]&quot; - dockerinaction/ch7_static &amp;lt; static_hello.tar&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드한 이미지를 실행해본다.&lt;/p&gt;
&lt;pre id=&quot;code_1733645269256&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container run dockerinaction/ch7_static&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hello 실행 파일을 진입점으로 하는 컨테이너가 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7.4 버전 관리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커로 다중 버전을 관리하기 위해서는 repository tagging이 중요하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.9와 같은 숫자형 버전을 태그에 기입하여 어떤 라이브러리 버전을 사용하는 지 명시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요약&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- docker conatimer commit 명령어는 변경사항이 반영된 새로운 이미지를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 컨테이너가 commit된 경우 해당 이미지 구성 요소로 실행을 하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이미지 레이어는 변경 내역이 적층된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이미지 사이즈는 구성 레이어 사이즈 의 합이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- docker image export, import는 이미지를 파일로 내보내고 파일로 부터 가져올 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- docker image tag 명령어는 하나의 repository에서 여러 태그를 부여할 때 사용 가능 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- stable 빌드 버전을 latest로 부여하라.&lt;/p&gt;</description>
      <category>Cloud/Docker</category>
      <author>로파이</author>
      <guid isPermaLink="true">https://narakit.tistory.com/312</guid>
      <comments>https://narakit.tistory.com/312#entry312comment</comments>
      <pubDate>Fri, 6 Dec 2024 22:33:29 +0900</pubDate>
    </item>
    <item>
      <title>Docker In Action 6. Limiting risk with resource controls</title>
      <link>https://narakit.tistory.com/311</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;A Process In Isolation, 프로세스 단위의 고립성 수준을 이용하는 도커 컨테이너&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 컨테이너의 8가지 주요 특징&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MNT - filesystem access and structure&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;chroot() - Controls location of filesystem root&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cgroups - Resource protection&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PID - Process identifiers and process capabilities&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NET - Network access and structure&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UTS - Host and domain name&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;USR - Usernames and identifiers&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPC - Communication by shared memory&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6.1 자원 사용 가능 제어&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 물리 머신에서 CPU 사용과 메모리의 자원은 한정적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6.1.1 메모리 제한&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커를 생성하거나 시작할 때 -m, --memory 옵션을 사용하면 메모리 사용 제한을 걸 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1733314954947&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container run -d
--name ch6_mariadb
--memory 256m
--cpu-shares 1024
--cap-drop net_raw
-e MYSQL_ROOT_PASSWORD=test
mariadb:5.5&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리 제한 옵션은 해당 메모리 크기 만큼 예약을 하는 것이 아니다. 과다 사용만을 방지하는 옵션이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과다 사용이 탐지되는 경우 리눅스 커널에서 이를 효율적으로 방지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어플리케이션마다 해당 과다 사용에 대한 에러 발생에 대한 처리가 필요할 수 있으며 얼마나 많은 메모리를 사용할 것 인가에 대한 판단이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6.1.2 CPU 제한&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU 사용에 대해 옵션으로 제어할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--cpu-shares 옵션은 도커 컨테이너가 다른 컨테이너 대비 얼마 만큼이나 CPU를 사용할 수 있는 지를 가중치로 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--cpus 옵션은 최대 x 개의 CPU를 사용할 수 있도록 하는 옵션이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--cpuset-cpus 옵션은 멀티 코어 시스템에서 특정 CPU를 사용하고 context-switch 비용을 낮추도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 디바이스 접근 제어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트에 설치된 웹캠과 같은 디바이스를 컨테이너에 주입하여 사용할 수 있도록 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1733315702653&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container run -it --rm --device /dev/video0:/dev/video0 ubuntu:16.04 ls -al /dev&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6.2 메모리 공유&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스 간 메모리를 공유(interprocess communication)하는 방법을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPC를 사용하는 도커는 각 컨테이너마다 고유의 IPC 이름 공간을 생성한다. linux가 제공하는 IPC는 이름 공간으로 분리된 shared memory, 세마 포어, 메시지 큐와 같은 기본적인 공유 메모리를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1733316086363&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container run -d -u nobody --name ch6_ipc_producer --ipc shareable dockerinaction/ch6_ipc -producer&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1733316119932&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container run -d -u nobody --name ch6_ipc_consumer --ipc container:ch6_ipc_producer dockerinaction/ch6_ipc -consumer&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ch6_ipc_producer 컨테이너는 &quot;shareable&quot; 공유 가능한 ipc를 가진 컨테이너이다. (--ipc shareable)&lt;/li&gt;
&lt;li&gt;ch6_ipc_consumer 컨테이너는&amp;nbsp; ch6_ipc_producer 컨테이너의 IPC를 조인하여 실행한다. (--ipc container:ch6_ipc_producer)&lt;/li&gt;
&lt;li&gt;--ipc=host 옵션의 경우 호스트와 공유 가능하다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;757&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2JYJy/btsK7Q5jHWM/ks0zq6e2V0UFZojTZ5hrck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2JYJy/btsK7Q5jHWM/ks0zq6e2V0UFZojTZ5hrck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2JYJy/btsK7Q5jHWM/ks0zq6e2V0UFZojTZ5hrck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2JYJy%2FbtsK7Q5jHWM%2Fks0zq6e2V0UFZojTZ5hrck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;757&quot; height=&quot;495&quot; data-origin-width=&quot;757&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고: &lt;a href=&quot;https://docs.docker.com/reference/cli/docker/container/run/#ipc&quot;&gt;docker run | Docker Docs&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6.3 Users&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 컨테이너를 시작하면 이미지 메타데이터에 기록된 기본 유저를 사용한다. (주로 root 유저)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;root 유저는 컨테이너 상태에 모든 접근 권한을 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지를 pull 한 이후 inspect 명령어를 사용하여 유저 이름을 확인할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1733317081033&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker image pull busybox:1.29
docker image inspect busybox:1.29
docker inspect --format &quot;{{.Config.User}}&quot; busybox:1.29&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과가 비어있다면, 컨테이너는 root 유저로 실행 중인 것이다. 그렇지 않다면, 이미지에 run-as 로 지정된 유저를 사용하거나 컨테이너를 생성할 때 지정한 유저가 출력된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;linux는 미리 정의된 유저들을 사용한다. 다음과 같이 이미지에서 사용가능한 유저들을 알아 볼 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1733317635475&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker contaienr run --rm busybox:1.29 awk -F: '$0=$1' /etc/passwd&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;649&quot; data-origin-height=&quot;143&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/egu5VB/btsK78klOx7/nOKLFrSzaiuYPTtn9fQww1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/egu5VB/btsK78klOx7/nOKLFrSzaiuYPTtn9fQww1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/egu5VB/btsK78klOx7/nOKLFrSzaiuYPTtn9fQww1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fegu5VB%2FbtsK78klOx7%2FnOKLFrSzaiuYPTtn9fQww1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;649&quot; height=&quot;143&quot; data-origin-width=&quot;649&quot; data-origin-height=&quot;143&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 리스트 중 유저를 선택하여 컨테이너를 시작한다면, 다음과 같이 -u 혹은 --user 옵션을 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1733318055002&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container run --rm --user nobody busybox:1.29 id&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저 이름을 지정하는 대신 UID를 직접 지정하여 유저를 설정할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1733318431579&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container run --rm -u nobody:nogroup busybox:1.29 id
docker container run --rm -u 10000:20000 busybox:1.29 id&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;user:group 혹은 UID:GID, 유저와 그룹을 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6.3.2 유저와 볼륨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;볼륨으로 마운트한 파일에 호스트의&amp;nbsp; 유저 중 하나로 소유자가 지정된 경우, 컨테이너에 지정된 유저가 접근이 허용되지 않을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;root 유저 권한으로 지정한 파일을 컨테이너에 마운트 한 경우 같은 파일 권한을 가진다. 즉, root 유저가 아니라면 파일 접근이 허용되지 않게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6.3.3 linux user namespace와 UID remapping&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;linux의 user namespace(USR)는 유저를 한 이름 공간에서 다른 이름 공간의 유저로 맵핑한다. user namespace는 PID namspace 와 같이 컨테이너 UIDs와 GIDs는 호스트의 기본 개체들로 부터 할당된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커는 USR namespace 기능을 기본값으로 사용하지 않는다. 그렇게 해야 호스트로 부터 마운트한 파일들 권한 유저를 컨테이너에서도 같은 유저 (UID)로 유지 할 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;USR namespace 기능은 호스트 유저 중 권한을 상속받지 않은 유저를 컨테이너에서 사용하려 할 때 동작한다. 기능은 subuid와 subgid을 정의하고 usernamespace remapping을 한다. 그리고 도커 데몬 서비스의 userns-remap 설정을 구성한다. 맵핑은 호스트의 user namespace 에서 컨테이너의 user namspace 맵핑을 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 호스트의 5000 번 부터 시작하는 UID는 총 1000개의 UID 에 대해 컨테이너 내부에서는 0번 부터 1000번대까지로 맵핑한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6.4 OS 기능 사용하기 (capabilities)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;host가 linux 일 경우 capabilties 기능은 컨테이너의 OS 기능을 선택적으로 상속한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너를 시작할 경우, 도커는 특별히 어플리케이션에 필요하여 명시적으로 지정하지 않은 경우를 제외 하고 모든 capabilites를 drop 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;drop 되는 capbilities는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SYS_MODULE - Insert/remove kernel modules&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SYS_RAWIO Modify kerner memory&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SYS_NICE Modify priorty of processes&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SYS_RESOURCE override resource limits&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 capabilities를 drop 하고자하는 경우 --cap-drop 옵션을 지정하여 컨테이너를 실행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1733321541693&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container run --rm -u nobody \
--cap-drop net_raw ubuntu:16.04 /bin/bash -c &quot;capsh --print | grep net_raw&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--cap-add 옵션은 반대로 capability를 컨테이너로 상속한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6.5 full previlege로 컨테이너 실행하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 관리자로서 컨테이너 내부에 작업을 수행해야할 때 유용하다. full previlege 컨테이너는 고립된 파일 시스템과 네트워크를 유지하지만 공유 메모리와 디바이스에 대한 전체 접근 권한과 system capabilites를 모두 획득한다.&lt;/p&gt;
&lt;pre id=&quot;code_1733322238525&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container run --rm --priviledged ubuntu:16.04 id // check IDs
docker container run --rm --priviledged ubuntu:16.04 cpash --print  // check capabilities
docker container run --rm --priviledged ubuntu:16.04 ls /dev // check list of mounted devices&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6.6 고급 도구로 컨테이너 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현대 linux 커널은 seccomp 도구를 제공하는데 강력한 시스템 보안 기능을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커의 기본 seccomp는 40 개의 커널 시스템 콜을 사용하지 못 하게 한다. 또한 추가 도구를 사용하여 도커 컨테이너를 강화할 수 있다. 예를 들면, seccomp 프로필을 커스터마이즈 하거나 AppArmor, SELinux 와 같은 도구가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6.6.1 추가 보안 옵션 지정하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--security-opt 플래그는 리눅스의 seccomp 와 Linux Security Modules (LSM) 기능을 구성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Seccomp은 리눅스 시스템 콜을 제어한다. 도커의 기본 seccomp 프로필은 기본적으로 모든 시스템 콜을 차단하며 260개의 명시한 시스템 콜만 안전하다고 보고 허용한다. 44개의 시스템 콜은 보통 프로그램에서 호출되지 않게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;seccomp 프로필을 설정하려면 다음과 같이 커스텀 프로필 파일을 제공한다.&lt;/p&gt;
&lt;pre id=&quot;code_1733322804939&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker contaienr run --rm -t --security-opt seccomp=&amp;lt;FULL_PATH_TO_PROFILE&amp;gt; ubuntu:16.04 sh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Linux Security Modules (LSM)는 프레임워크로 리눅스 OS와 보안 기능 공급자 사이의 인터페이스를 제공한다. AppArmor와 SELinux는 그러한 보안 기능 공급자 (security providers)이다. ACL이나 MAC 주소 검증 그리고 기본 리눅스 discrtionary access control을 대체하는 기능을 제공한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;6.7 어플레케이션 고려 사항&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;해커에게 취약하지 않은 어플리케이션을 실행하고 운영하는 방법&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;- 제한된 권한을 가진 유저로 어플리케이션을 실행한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;- system capabilites를 제한한다. 시스템 구성 설정이 보호될 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;- CPU와 메모리 사용량을 제한한다. 호스트 머신의 필수적인 프로세스들의 높은 반응성을 유지시켜줄 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;- 접근하는 디바이스를 제어한다. webcam, usb를 목적에 맞지 않은 사용을 방지하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;요약&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;- 도커는 cgroup을 사용하여 메모리 제한과 CPU 가중치 할당, 제한, 코어 할당, 디바이스 접근 제어를 가능하게 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;- 각 컨테이너는 고유의 IPC 이름 공간을 가지며, 다른 컨테이너 혹은 호스트와 공유하여 공유 메모리를 접근할 수 있게 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;- 도커는 격리된 USR namespace를 사용한다. 기본적으로 호스트와 같은 user, group ID를 사용하지만 호스트에 있지 않은 컨테이너의 user, group ID는 호스트로 부터 재맵핑된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;- --user 옵션으로 root 유저가 아닌 컨테이너를 실행할 유저를 지정할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;- previledge 모드 관리자 작업을 가능하게 한다 가능하다면 previledge 모드로 컨테이너를 실행하지 말자.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;- linux capabilites는 OS 기능 권한을 제공한다. 도커는 격리의 이유로 몇몇 capabilites를 호스트로부터 상속하지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;- capbilites 상속 혹은 제거는 --cap-add 또는 --cap-drop으로 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;- 더 격리된 환경을 제공하고 높은 보안 수준을 위해 seccomp, SELinux, AppArmor와 같은 도구를 같이 사용하여 컨테이너 실행하는 것을 고려하라.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Cloud/Docker</category>
      <author>로파이</author>
      <guid isPermaLink="true">https://narakit.tistory.com/311</guid>
      <comments>https://narakit.tistory.com/311#entry311comment</comments>
      <pubDate>Wed, 4 Dec 2024 23:59:58 +0900</pubDate>
    </item>
    <item>
      <title>[C++] Memory Pool</title>
      <link>https://narakit.tistory.com/306</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;메모리 풀 구현&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;32/64/128/... 바이트 단위로 메모리를 미리 할당하여, 실제 할당할 때 해당 메모리를 사용함.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;장점&lt;/b&gt;&lt;br /&gt;메모리 단편화 문제를 완화 가능. 특히 사이즈가 큰 메모리를 할당할 때 적절한 위치의 페이지를 찾는 것이 시간이 소요될 수 있다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;단점&lt;/b&gt;&lt;br /&gt;사용하지 않는 메모리를 미리 할당하여 사용하게 되는 낭비.&lt;br /&gt;메모리 풀과 같이 특정 할당자(Allocator)를 사용한 경우, 메모리를 반납하여 재사용할 수 있도록 할당자를 통한 해제를 해야한다.&lt;br /&gt;&lt;br /&gt;std::shared_ptr의 생성자에는 해제자 deleter에 대한 인스턴스를 받아 해제를 제어할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;994&quot; data-origin-height=&quot;162&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ca34k6/btsGdYVteHE/dSnXUFkwYfCM8dkBSx1UmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ca34k6/btsGdYVteHE/dSnXUFkwYfCM8dkBSx1UmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ca34k6/btsGdYVteHE/dSnXUFkwYfCM8dkBSx1UmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fca34k6%2FbtsGdYVteHE%2FdSnXUFkwYfCM8dkBSx1UmK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;994&quot; height=&quot;162&quot; data-origin-width=&quot;994&quot; data-origin-height=&quot;162&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;std::unique_ptr는 타입으로 선언하여 사용한다. 커스텀 deleter를 사용하는 경우 타입 이름이 지저분해질 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1138&quot; data-origin-height=&quot;143&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bl6y5Z/btsGePQV23l/PKQSee7YQxmM7JUDicDD41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bl6y5Z/btsGePQV23l/PKQSee7YQxmM7JUDicDD41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bl6y5Z/btsGePQV23l/PKQSee7YQxmM7JUDicDD41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbl6y5Z%2FbtsGePQV23l%2FPKQSee7YQxmM7JUDicDD41%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1138&quot; height=&quot;143&quot; data-origin-width=&quot;1138&quot; data-origin-height=&quot;143&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;104&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuRKXc/btsGc3Xid6S/nKnF9ECmMvKoInuGvy3OfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuRKXc/btsGc3Xid6S/nKnF9ECmMvKoInuGvy3OfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuRKXc/btsGc3Xid6S/nKnF9ECmMvKoInuGvy3OfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuRKXc%2FbtsGc3Xid6S%2FnKnF9ECmMvKoInuGvy3OfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;658&quot; height=&quot;90&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;104&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;MemoryHeader&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1711808625972&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma pack(push, 1)
struct DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) MemoryHeader {
	SLIST_ENTRY* next;
	struct Info {
		size_t id : 16;
		size_t size : 16;
		size_t tag : 32;
	} info;
	static constexpr size_t kSize = sizeof(SLIST_ENTRY*) + sizeof(Info);
};
#pragma pack(pop)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 메모리의 헤더 부분으로 기본 16바이트를 차지하게 된다.&lt;br /&gt;SLIST_ENTRY* next 부분은 windows의 InterlocekdSList를 사용하기 위한 필드.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*InterlockedSList는 lock-free FIFO 자료구조 API를 제공한다.&lt;br /&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/interlockedapi/&quot;&gt;Interlockedapi.h header - Win32 apps | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1711808831373&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Interlockedapi.h header - Win32 apps&quot; data-og-description=&quot;Table of contents Article 01/24/2023 Feedback In this article --&amp;gt; This header is used by System Services. For more information, see: interlockedapi.h contains the following programming interfaces: Functions &amp;nbsp; InitializeSListHead Initializes the head of a &quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/windows/win32/api/interlockedapi/&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/windows/win32/api/interlockedapi/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/OfDI5/hyVGOvZgwP/J6glVdjvGjm4vn4N5s61Z1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/interlockedapi/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/windows/win32/api/interlockedapi/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/OfDI5/hyVGOvZgwP/J6glVdjvGjm4vn4N5s61Z1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Interlockedapi.h header - Win32 apps&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Table of contents Article 01/24/2023 Feedback In this article --&amp;gt; This header is used by System Services. For more information, see: interlockedapi.h contains the following programming interfaces: Functions &amp;nbsp; InitializeSListHead Initializes the head of a&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;StaticMemoryPool&lt;/b&gt;&lt;br /&gt;필요한 페이지를 미리 할당하여 Memory 사이즈 단위로 미리 잘라서 쓰는 방식&lt;/p&gt;
&lt;pre id=&quot;code_1711808968695&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class StaticMemoryPool {
public:
	struct BlockAlloc {
		size_t size;
		size_t count;
	};

	static constexpr size_t kMaxBlockSize = 0x4000;
    //...

	StaticMemoryPool(size_t block_size, size_t target_block_num)
		:
		_block_size(block_size) {
		_address_begin = MemoryManager::Get().request(nullptr, _block_size, target_block_num, _alloc_size);
		_address_end = reinterpret_cast&amp;lt;char*&amp;gt;(_address_begin) + _alloc_size;
		_alloc_count = _alloc_size / _block_size;
		for (size_t i = 0; i &amp;lt; _alloc_count; ++i) {
			auto* header = reinterpret_cast&amp;lt;MemoryHeader*&amp;gt;(reinterpret_cast&amp;lt;char*&amp;gt;(_address_begin) + i * _block_size);
			header-&amp;gt;next = nullptr;
			header-&amp;gt;info.tag = kFreeTag;
			header-&amp;gt;info.id = 0;
			header-&amp;gt;info.size = 0;
			_free_list.push(header);
		}
	}

   //...
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;예를 들면, 32바이트 단위의 메모리를 연속된 주소로 1024개를 할당하여 미리 가지고 있는다.&lt;br /&gt;각 32바이트의 첫 16바이트는 메모리 헤더로 사용하고 초기화시 SList 인스턴스인 _free_list에 넣어둔다.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;allocate 및 dellocate&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1711809071306&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	void* allocate(size_t size) noexcept {
		auto header = _free_list.pop();
		if (header == nullptr) {
			return ::malloc(size);
		}
#ifdef _DEBUG
		assert(header-&amp;gt;info.tag == kFreeTag);
		header-&amp;gt;info.tag = kAllocTag;
		header-&amp;gt;info.id = _id_counter.fetch_add(1, std::memory_order_relaxed);
		header-&amp;gt;info.size = size;
#endif // !_DEBUG
		return reinterpret_cast&amp;lt;char*&amp;gt;(header) + MemoryHeader::kSize;
	}

	void  deallocate(void* ptr, size_t size) noexcept {
		if (contains(ptr) == false) {
			::free(ptr);
			return;
		}
		auto* header = reinterpret_cast&amp;lt;MemoryHeader*&amp;gt;(reinterpret_cast&amp;lt;char*&amp;gt;(ptr) - MemoryHeader::kSize);
#ifdef _DEBUG
		assert(header-&amp;gt;info.tag == kAllocTag);
		header-&amp;gt;info.tag = kFreeTag;
#endif // !_DEBUG
		_free_list.push(header);
	}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;size 만큼 할당 요청에 대하여 _free_list로 부터 여분의 메모리가 있으면 해당 메모리를 반환한다.&lt;br /&gt;여분이 없다면 malloc을 사용하여 기본 내장 할당 함수를 사용한다.&lt;br /&gt;&lt;br /&gt;해제 시 해당 메모리 주소를 기준으로 자신이 관리하는 메모리 주소 영역을 판단하여 다시 반납하거나 아니라면 free 함수를 통해 직접 해제한다.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;std::allocator_traits&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;std 컨테이너를 정의할 때 흔히 원소에 대한 첫 타입 외 Allocator 타입을 받을 수 있는데&lt;br /&gt;커스텀 할당자를 정의하여 컨테이너 원소를 할당할 때 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;344&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cn0fiH/btsGfv5AdyX/H2NepqO5MkbPk7EKXtHEY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cn0fiH/btsGfv5AdyX/H2NepqO5MkbPk7EKXtHEY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cn0fiH/btsGfv5AdyX/H2NepqO5MkbPk7EKXtHEY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcn0fiH%2FbtsGfv5AdyX%2FH2NepqO5MkbPk7EKXtHEY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1010&quot; height=&quot;344&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;344&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 커스텀 할당자는 std::allocator_traits 제약을 만족해야한다.&lt;br /&gt;- 참고&lt;br /&gt;&lt;a href=&quot;https://en.cppreference.com/w/cpp/memory/allocator_traits&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://en.cppreference.com/w/cpp/memory/allocator_traits&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/ko-kr/cpp/standard-library/allocator-traits-class?view=msvc-170&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://learn.microsoft.com/ko-kr/cpp/standard-library/allocator-traits-class?view=msvc-170&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 예시&lt;br /&gt;MemoryPool을 사용하는 StaticAllocator&lt;/p&gt;
&lt;pre id=&quot;code_1711809694017&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename T&amp;gt;
class StaticAllocator {
public:
	using allocator_type = StaticAllocator&amp;lt;T&amp;gt;;
	using value_type = T;
	using size_type = size_t;
	using difference_type = ptrdiff_t;
	using pointer = T*;
	using const_pointer = const T*;
	using reference = T&amp;amp;;
	using const_reference = const T&amp;amp;;

	StaticAllocator() = default;
	~StaticAllocator() = default;

	StaticAllocator(const StaticAllocator&amp;amp;) noexcept {};
	StaticAllocator&amp;amp; operator=(const StaticAllocator&amp;amp;) noexcept { return *this; }

	template&amp;lt;typename U&amp;gt;
	StaticAllocator(const StaticAllocator&amp;lt;U&amp;gt;&amp;amp;) noexcept {}
	template&amp;lt;typename U&amp;gt;
	StaticAllocator&amp;amp; operator=(const StaticAllocator&amp;lt;U&amp;gt;&amp;amp;) noexcept { return *this; }

	constexpr pointer address(reference x) const noexcept { return &amp;amp;x; }
	constexpr const_pointer address(const_reference x) const noexcept { return &amp;amp;x; }

	pointer allocate(size_type n, const void* = 0) {
		size_t alloc_size = n * sizeof(T);
		if (alloc_size &amp;gt; StaticMemoryPool::kMaxAllocSize) {
			return static_cast&amp;lt;pointer&amp;gt;(::malloc(alloc_size));
		}
		return static_cast&amp;lt;pointer&amp;gt;(StaticMemoryPool::g_memory_mapper.Get(alloc_size).allocate(alloc_size));
	}

	void deallocate(pointer p, size_type n) {
		size_t alloc_size = n * sizeof(T);
		if (alloc_size &amp;gt; StaticMemoryPool::kMaxAllocSize) {
			return ::free(p);
		}
		StaticMemoryPool::g_memory_mapper.Get(alloc_size).deallocate(p, alloc_size);
	}

	template&amp;lt;typename ...Args&amp;gt;
	void construct(pointer p, Args&amp;amp;&amp;amp;... args) {
		new (p) T(std::forward&amp;lt;Args&amp;gt;(args)...);
	}

	void destroy(pointer p) {
		p-&amp;gt;~T();
	}
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;- allocate(size_type, const void* hint)&lt;br /&gt;n 개의 초기화 되지 않은 메모리를 할당한다. 이전 할당 주소 등을 가리켜 hint는 할당하고자하는 메모리 주소에 대한 힌트를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열과 같이 한 번에 여러 개를 요청할 때만 n &amp;gt; 1이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;- deallocate(pointer p, size_type n)&lt;br /&gt;&amp;nbsp;p 주소에 대한 메모리를 해제한다.&lt;br /&gt;&lt;br /&gt;- construct&lt;br /&gt;new 연산자와 같이 할당된 메모리에 대한 주소에 초기화 작업을 수행한다.&lt;br /&gt;&lt;br /&gt;- destroy&lt;br /&gt;개체 파괴를 진행하여 메모리를 해제하기 전에 소멸자와 같이 정리 작업을 수행한다.&lt;br /&gt;&lt;br /&gt;위 allocator 타입을 사용하여 shared_ptr 인스턴스를 생성하는 예시&lt;/p&gt;
&lt;pre id=&quot;code_1711810991409&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct SharedPtrFactory {
	template&amp;lt;typename T, template &amp;lt;typename...&amp;gt; typename Allocator = std::allocator, typename ...Args&amp;gt;
	static inline std::shared_ptr&amp;lt;T&amp;gt; Execute(Args&amp;amp;&amp;amp;... args) {
		Allocator&amp;lt;T&amp;gt; allocator;
		return std::allocate_shared&amp;lt;T&amp;gt;(allocator, std::forward&amp;lt;Args&amp;gt;(args)...);
	}
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;테스트&lt;/b&gt;&lt;br /&gt;std::allocator, std::pmr::polymorphic_allocator, StaticAllocator, DynamicAllocator (요청할 때 메모리를 할당하여 재사용)&lt;br /&gt;백만번의 컨테이너 요소 삽입과 삭제에서 더 빠른 성능을 보인다.&lt;br /&gt;특정 사이즈의 메모리를 많이 사용할 것을 미리 알 수 있다면, 더 많은 메모리를 미리 할당하여 사용할 때 유리하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;377&quot; data-origin-height=&quot;377&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s65vX/btsGejrr3wA/GCZTFLZTBvzSIJiWZz6Cl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s65vX/btsGejrr3wA/GCZTFLZTBvzSIJiWZz6Cl0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s65vX/btsGejrr3wA/GCZTFLZTBvzSIJiWZz6Cl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs65vX%2FbtsGejrr3wA%2FGCZTFLZTBvzSIJiWZz6Cl0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;377&quot; height=&quot;377&quot; data-origin-width=&quot;377&quot; data-origin-height=&quot;377&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;테스트 코드&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/cjbMXG/btsGfeQsUIR/bs6jyUAkk4SZjXSEDZa0Z1/Allocator.zip?attach=1&amp;amp;knm=tfile.zip&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;Allocator.zip&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.01MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Advanced C++</category>
      <author>로파이</author>
      <guid isPermaLink="true">https://narakit.tistory.com/306</guid>
      <comments>https://narakit.tistory.com/306#entry306comment</comments>
      <pubDate>Sun, 31 Mar 2024 00:15:21 +0900</pubDate>
    </item>
  </channel>
</rss>